qcacld-3.0: Initial snapshot of ihelium wlan driver
qcacld-3.0: Initial snapshot of ihelium wlan driver
to match code-scanned SU Release 5.0.0.139. This is
open-source version of wlan for next Android release.
Change-Id: Icf598ca97da74f84bea607e4e902d1889806f507
diff --git a/core/hif/src/ce/ce_api.h b/core/hif/src/ce/ce_api.h
new file mode 100644
index 0000000..8bde74d
--- /dev/null
+++ b/core/hif/src/ce/ce_api.h
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2013-2015 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.
+ */
+
+#ifndef __COPY_ENGINE_API_H__
+#define __COPY_ENGINE_API_H__
+
+#include "ce_main.h"
+/* TBDXXX: Use int return values for consistency with Target */
+
+/* TBDXXX: Perhaps merge Host/Target-->common */
+
+/*
+ * Copy Engine support: low-level Target-side Copy Engine API.
+ * This is a hardware access layer used by code that understands
+ * how to use copy engines.
+ */
+
+/*
+ * A "struct CE_handle *" serves as an opaque pointer-sized
+ * handle to a specific copy engine.
+ */
+struct CE_handle;
+
+/*
+ * "Send Completion" callback type for Send Completion Notification.
+ *
+ * If a Send Completion callback is registered and one or more sends
+ * have completed, the callback is invoked.
+ *
+ * per_ce_send_context is a context supplied by the calling layer
+ * (via ce_send_cb_register). It is associated with a copy engine.
+ *
+ * per_transfer_send_context is context supplied by the calling layer
+ * (via the "send" call). It may be different for each invocation
+ * of send.
+ *
+ * The buffer parameter is the first byte sent of the first buffer
+ * sent (if more than one buffer).
+ *
+ * nbytes is the number of bytes of that buffer that were sent.
+ *
+ * transfer_id matches the value used when the buffer or
+ * buf_list was sent.
+ *
+ * Implementation note: Pops 1 completed send buffer from Source ring
+ */
+typedef void (*ce_send_cb)(struct CE_handle *copyeng,
+ void *per_ce_send_context,
+ void *per_transfer_send_context,
+ cdf_dma_addr_t buffer,
+ unsigned int nbytes,
+ unsigned int transfer_id,
+ unsigned int sw_index,
+ unsigned int hw_index,
+ uint32_t toeplitz_hash_result);
+
+/*
+ * "Buffer Received" callback type for Buffer Received Notification.
+ *
+ * Implementation note: Pops 1 completed recv buffer from Dest ring
+ */
+typedef void (*CE_recv_cb)(struct CE_handle *copyeng,
+ void *per_CE_recv_context,
+ void *per_transfer_recv_context,
+ cdf_dma_addr_t buffer,
+ unsigned int nbytes,
+ unsigned int transfer_id,
+ unsigned int flags);
+
+/*
+ * Copy Engine Watermark callback type.
+ *
+ * Allows upper layers to be notified when watermarks are reached:
+ * space is available and/or running short in a source ring
+ * buffers are exhausted and/or abundant in a destination ring
+ *
+ * The flags parameter indicates which condition triggered this
+ * callback. See CE_WM_FLAG_*.
+ *
+ * Watermark APIs are provided to allow upper layers "batch"
+ * descriptor processing and to allow upper layers to
+ * throttle/unthrottle.
+ */
+typedef void (*CE_watermark_cb)(struct CE_handle *copyeng,
+ void *per_CE_wm_context, unsigned int flags);
+
+#define CE_WM_FLAG_SEND_HIGH 1
+#define CE_WM_FLAG_SEND_LOW 2
+#define CE_WM_FLAG_RECV_HIGH 4
+#define CE_WM_FLAG_RECV_LOW 8
+
+/* A list of buffers to be gathered and sent */
+struct ce_sendlist;
+
+/* Copy Engine settable attributes */
+struct CE_attr;
+
+/*==================Send=====================================================*/
+
+/* ce_send flags */
+/* disable ring's byte swap, even if the default policy is to swap */
+#define CE_SEND_FLAG_SWAP_DISABLE 1
+
+/*
+ * Queue a source buffer to be sent to an anonymous destination buffer.
+ * copyeng - which copy engine to use
+ * buffer - address of buffer
+ * nbytes - number of bytes to send
+ * transfer_id - arbitrary ID; reflected to destination
+ * flags - CE_SEND_FLAG_* values
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Note: If no flags are specified, use CE's default data swap mode.
+ *
+ * Implementation note: pushes 1 buffer to Source ring
+ */
+int ce_send(struct CE_handle *copyeng,
+ void *per_transfer_send_context,
+ cdf_dma_addr_t buffer,
+ unsigned int nbytes,
+ unsigned int transfer_id,
+ unsigned int flags,
+ unsigned int user_flags);
+
+#ifdef WLAN_FEATURE_FASTPATH
+int ce_send_fast(struct CE_handle *copyeng, cdf_nbuf_t *msdus,
+ unsigned int num_msdus, unsigned int transfer_id);
+
+#endif
+void ce_pkt_dl_len_set(void *hif_sc, unsigned int pkt_download_len);
+
+/*
+ * Register a Send Callback function.
+ * This function is called as soon as the contents of a Send
+ * have reached the destination, unless disable_interrupts is
+ * requested. In this case, the callback is invoked when the
+ * send status is polled, shortly after the send completes.
+ */
+void ce_send_cb_register(struct CE_handle *copyeng,
+ ce_send_cb fn_ptr,
+ void *per_ce_send_context, int disable_interrupts);
+
+/*
+ * Return the size of a SendList. This allows the caller to allocate
+ * a SendList while the SendList structure remains opaque.
+ */
+unsigned int ce_sendlist_sizeof(void);
+
+/* Initialize a sendlist */
+void ce_sendlist_init(struct ce_sendlist *sendlist);
+
+/* Append a simple buffer (address/length) to a sendlist. */
+int ce_sendlist_buf_add(struct ce_sendlist *sendlist,
+ cdf_dma_addr_t buffer,
+ unsigned int nbytes,
+ uint32_t flags, /* OR-ed with internal flags */
+ uint32_t user_flags);
+
+/*
+ * Queue a "sendlist" of buffers to be sent using gather to a single
+ * anonymous destination buffer
+ * copyeng - which copy engine to use
+ * sendlist - list of simple buffers to send using gather
+ * transfer_id - arbitrary ID; reflected to destination
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes multiple buffers with Gather to Source ring.
+ */
+int ce_sendlist_send(struct CE_handle *copyeng,
+ void *per_transfer_send_context,
+ struct ce_sendlist *sendlist,
+ unsigned int transfer_id);
+
+/*==================Recv=====================================================*/
+
+/*
+ * Make a buffer available to receive. The buffer must be at least of a
+ * minimal size appropriate for this copy engine (src_sz_max attribute).
+ * copyeng - which copy engine to use
+ * per_transfer_recv_context - context passed back to caller's recv_cb
+ * buffer - address of buffer in CE space
+ * Returns 0 on success; otherwise an error status.
+ *
+ * Implemenation note: Pushes a buffer to Dest ring.
+ */
+int ce_recv_buf_enqueue(struct CE_handle *copyeng,
+ void *per_transfer_recv_context,
+ cdf_dma_addr_t buffer);
+
+/*
+ * Register a Receive Callback function.
+ * This function is called as soon as data is received
+ * from the source.
+ */
+void ce_recv_cb_register(struct CE_handle *copyeng,
+ CE_recv_cb fn_ptr,
+ void *per_CE_recv_context,
+ int disable_interrupts);
+
+/*==================CE Watermark=============================================*/
+
+/*
+ * Register a Watermark Callback function.
+ * This function is called as soon as a watermark level
+ * is crossed. A Watermark Callback function is free to
+ * handle received data "en masse"; but then some coordination
+ * is required with a registered Receive Callback function.
+ * [Suggestion: Either handle Receives in a Receive Callback
+ * or en masse in a Watermark Callback; but not both.]
+ */
+void ce_watermark_cb_register(struct CE_handle *copyeng,
+ CE_watermark_cb fn_ptr,
+ void *per_CE_wm_context);
+
+/*
+ * Set low/high watermarks for the send/source side of a copy engine.
+ *
+ * Typically, the destination side CPU manages watermarks for
+ * the receive side and the source side CPU manages watermarks
+ * for the send side.
+ *
+ * A low watermark of 0 is never hit (so the watermark function
+ * will never be called for a Low Watermark condition).
+ *
+ * A high watermark equal to nentries is never hit (so the
+ * watermark function will never be called for a High Watermark
+ * condition).
+ */
+void ce_send_watermarks_set(struct CE_handle *copyeng,
+ unsigned int low_alert_nentries,
+ unsigned int high_alert_nentries);
+
+/* Set low/high watermarks for the receive/destination side of copy engine. */
+void ce_recv_watermarks_set(struct CE_handle *copyeng,
+ unsigned int low_alert_nentries,
+ unsigned int high_alert_nentries);
+
+/*
+ * Return the number of entries that can be queued
+ * to a ring at an instant in time.
+ *
+ * For source ring, does not imply that destination-side
+ * buffers are available; merely indicates descriptor space
+ * in the source ring.
+ *
+ * For destination ring, does not imply that previously
+ * received buffers have been processed; merely indicates
+ * descriptor space in destination ring.
+ *
+ * Mainly for use with CE Watermark callback.
+ */
+unsigned int ce_send_entries_avail(struct CE_handle *copyeng);
+unsigned int ce_recv_entries_avail(struct CE_handle *copyeng);
+
+/*
+ * Return the number of entries in the ring that are ready
+ * to be processed by software.
+ *
+ * For source ring, the number of descriptors that have
+ * been completed and can now be overwritten with new send
+ * descriptors.
+ *
+ * For destination ring, the number of descriptors that
+ * are available to be processed (newly received buffers).
+ */
+unsigned int ce_send_entries_done(struct CE_handle *copyeng);
+unsigned int ce_recv_entries_done(struct CE_handle *copyeng);
+
+/* recv flags */
+/* Data is byte-swapped */
+#define CE_RECV_FLAG_SWAPPED 1
+
+void ce_enable_msi(struct ol_softc *scn,
+ unsigned int CE_id,
+ uint32_t msi_addr_lo,
+ uint32_t msi_addr_hi,
+ uint32_t msi_data);
+/*
+ * Supply data for the next completed unprocessed receive descriptor.
+ *
+ * For use
+ * with CE Watermark callback,
+ * in a recv_cb function when processing buf_lists
+ * in a recv_cb function in order to mitigate recv_cb's.
+ *
+ * Implemenation note: Pops buffer from Dest ring.
+ */
+int ce_completed_recv_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *flagsp);
+
+/*
+ * Supply data for the next completed unprocessed send descriptor.
+ *
+ * For use
+ * with CE Watermark callback
+ * in a send_cb function in order to mitigate send_cb's.
+ *
+ * Implementation note: Pops 1 completed send buffer from Source ring
+ */
+int ce_completed_send_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *sw_idx,
+ unsigned int *hw_idx,
+ uint32_t *toeplitz_hash_result);
+
+/*==================CE Engine Initialization=================================*/
+
+/* Initialize an instance of a CE */
+struct CE_handle *ce_init(struct ol_softc *scn,
+ unsigned int CE_id, struct CE_attr *attr);
+
+/*==================CE Engine Shutdown=======================================*/
+/*
+ * Support clean shutdown by allowing the caller to revoke
+ * receive buffers. Target DMA must be stopped before using
+ * this API.
+ */
+CDF_STATUS
+ce_revoke_recv_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp);
+
+/*
+ * Support clean shutdown by allowing the caller to cancel
+ * pending sends. Target DMA must be stopped before using
+ * this API.
+ */
+CDF_STATUS
+ce_cancel_send_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ uint32_t *toeplitz_hash_result);
+
+void ce_fini(struct CE_handle *copyeng);
+
+/*==================CE Interrupt Handlers====================================*/
+void ce_per_engine_service_any(int irq, struct ol_softc *scn);
+int ce_per_engine_service(struct ol_softc *scn, unsigned int CE_id);
+void ce_per_engine_servicereap(struct ol_softc *scn, unsigned int CE_id);
+
+/*===================CE cmpl interrupt Enable/Disable =======================*/
+void ce_disable_any_copy_compl_intr(struct ol_softc *scn);
+void ce_enable_any_copy_compl_intr(struct ol_softc *scn);
+void ce_disable_any_copy_compl_intr_nolock(struct ol_softc *scn);
+void ce_enable_any_copy_compl_intr_nolock(struct ol_softc *scn);
+
+/* API to check if any of the copy engine pipes has
+ * pending frames for prcoessing
+ */
+bool ce_get_rx_pending(struct ol_softc *scn);
+
+/* CE_attr.flags values */
+#define CE_ATTR_NO_SNOOP 0x01 /* Use NonSnooping PCIe accesses? */
+#define CE_ATTR_BYTE_SWAP_DATA 0x02 /* Byte swap data words */
+#define CE_ATTR_SWIZZLE_DESCRIPTORS 0x04 /* Swizzle descriptors? */
+#define CE_ATTR_DISABLE_INTR 0x08 /* no interrupt on copy completion */
+#define CE_ATTR_ENABLE_POLL 0x10 /* poll for residue descriptors */
+
+/* Attributes of an instance of a Copy Engine */
+struct CE_attr {
+ unsigned int flags; /* CE_ATTR_* values */
+ unsigned int priority; /* TBD */
+ unsigned int src_nentries; /* #entries in source ring -
+ * Must be a power of 2 */
+ unsigned int src_sz_max; /* Max source send size for this CE.
+ * This is also the minimum size of
+ * a destination buffer. */
+ unsigned int dest_nentries; /* #entries in destination ring -
+ * Must be a power of 2 */
+ void *reserved; /* Future use */
+};
+
+/*
+ * When using sendlist_send to transfer multiple buffer fragments, the
+ * transfer context of each fragment, except last one, will be filled
+ * with CE_SENDLIST_ITEM_CTXT. CE_completed_send will return success for
+ * each fragment done with send and the transfer context would be
+ * CE_SENDLIST_ITEM_CTXT. Upper layer could use this to identify the
+ * status of a send completion.
+ */
+#define CE_SENDLIST_ITEM_CTXT ((void *)0xcecebeef)
+
+/*
+ * This is an opaque type that is at least large enough to hold
+ * a sendlist. A sendlist can only be accessed through CE APIs,
+ * but this allows a sendlist to be allocated on the run-time
+ * stack. TBDXXX: un-opaque would be simpler...
+ */
+struct ce_sendlist {
+ unsigned int word[62];
+};
+
+#define ATH_ISR_NOSCHED 0x0000 /* Do not schedule bottom half/DPC */
+#define ATH_ISR_SCHED 0x0001 /* Schedule the bottom half for execution */
+#define ATH_ISR_NOTMINE 0x0002 /* for shared IRQ's */
+
+#ifdef IPA_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_ipa_get_resource(struct CE_handle *ce,
+ uint32_t *ce_sr_base_paddr,
+ uint32_t *ce_sr_ring_size,
+ cdf_dma_addr_t *ce_reg_paddr);
+#else
+static inline void ce_ipa_get_resource(struct CE_handle *ce,
+ uint32_t *ce_sr_base_paddr,
+ uint32_t *ce_sr_ring_size,
+ cdf_dma_addr_t *ce_reg_paddr)
+{
+ return;
+}
+#endif /* IPA_OFFLOAD */
+
+static inline void ce_pkt_error_count_incr(
+ struct HIF_CE_state *_hif_state,
+ enum ol_ath_hif_pkt_ecodes _hif_ecode)
+{
+ if (_hif_ecode == HIF_PIPE_NO_RESOURCE)
+ (_hif_state->scn->pkt_stats.hif_pipe_no_resrc_count)
+ += 1;
+}
+
+int hif_completion_thread(struct HIF_CE_state *hif_state);
+bool ce_check_rx_pending(struct ol_softc *scn, int ce_id);
+#if defined(FEATURE_LRO)
+void ce_lro_flush_cb_register(struct ol_softc *scn,
+ void (handler)(void *), void *data);
+#endif
+#endif /* __COPY_ENGINE_API_H__ */
diff --git a/core/hif/src/ce/ce_assignment.h b/core/hif/src/ce/ce_assignment.h
new file mode 100644
index 0000000..b493d55
--- /dev/null
+++ b/core/hif/src/ce/ce_assignment.h
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2014 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.
+ */
+
+/*
+ * Implementation of the Host-side Host InterFace (HIF) API
+ * for a Host/Target interconnect using Copy Engines over PCIe.
+ */
+
+#ifndef __HIF_PCI_INTERNAL_H__
+#define __HIF_PCI_INTERNAL_H__
+
+#define HIF_PCI_DEBUG ATH_DEBUG_MAKE_MODULE_MASK(0)
+#define HIF_PCI_IPA_UC_ASSIGNED_CE 5
+
+#if defined(DEBUG)
+static ATH_DEBUG_MASK_DESCRIPTION g_hif_debug_description[] = {
+ {HIF_PCI_DEBUG, "hif_pci"},
+};
+
+ATH_DEBUG_INSTANTIATE_MODULE_VAR(hif, "hif", "PCIe Host Interface",
+ ATH_DEBUG_MASK_DEFAULTS | ATH_DEBUG_INFO,
+ ATH_DEBUG_DESCRIPTION_COUNT
+ (g_hif_debug_description),
+ g_hif_debug_description);
+#endif
+
+#ifdef CONFIG_ATH_PCIE_ACCESS_DEBUG
+spinlock_t pcie_access_log_lock;
+unsigned int pcie_access_log_seqnum = 0;
+HIF_ACCESS_LOG pcie_access_log[PCIE_ACCESS_LOG_NUM];
+static void hif_target_dump_access_log(void);
+#endif
+
+/*
+ * Host software's Copy Engine configuration.
+ * This table is derived from the CE_PCI TABLE, above.
+ */
+#ifdef BIG_ENDIAN_HOST
+#define CE_ATTR_FLAGS CE_ATTR_BYTE_SWAP_DATA
+#else
+#define CE_ATTR_FLAGS 0
+#endif
+
+/* Maximum number of Copy Engine's supported */
+#define CE_HTT_H2T_MSG_SRC_NENTRIES 2048
+
+#define DIAG_CE_ID 7
+#define EPPING_CE_FLAGS_POLL \
+ (CE_ATTR_DISABLE_INTR|CE_ATTR_ENABLE_POLL|CE_ATTR_FLAGS)
+#ifdef QCA_WIFI_3_0
+static struct CE_attr host_ce_config_wlan[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 2048, 0, NULL,},
+ /* target->host HTT + HTC control */
+ { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 2048, 512, NULL,},
+ /* target->host WMI */
+ { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+ /* host->target WMI */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+ /* host->target HTT */
+ { /* CE4 */ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,
+ CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+ /* ipa_uc->target HTC control */
+ { /* CE5 */ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,
+ 1024, 512, 0, NULL,},
+ /* Target autonomous HIF_memcpy */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,
+ 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+static struct CE_pipe_config target_ce_config_wlan[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ 0, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host HTT */
+ { /* CE1 */ 1, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host WMI + HTC control */
+ { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target WMI */
+ { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target HTT */
+ { /* CE4 */ 4, PIPEDIR_OUT, 256, 256,
+ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,},
+ /* NB: 50% of src nentries, since tx has 2 frags */
+ /* ipa_uc->target */
+ { /* CE5 */ 5, PIPEDIR_OUT, 1024, 64,
+ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,},
+ /* Reserved for target autonomous HIF_memcpy */
+ { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+ /* CE7 used only by Host */
+ { /* CE7 */ 7, PIPEDIR_INOUT_H2H, 0, 0,
+ (CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR), 0,},
+ /* CE8 used only by IPA */
+ { /* CE8 */ 8, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}
+};
+
+static struct CE_attr host_ce_config_wlan_epping_poll[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 2048, 0, NULL,},
+ /* target->host EP-ping */
+ { /* CE1 */ EPPING_CE_FLAGS_POLL, 0, 0, 2048, 128, NULL,},
+ /* target->host EP-ping */
+ { /* CE2 */ EPPING_CE_FLAGS_POLL, 0, 0, 2048, 128, NULL,},
+ /* host->target EP-ping */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* host->target EP-ping */
+ { /* CE4 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* unused */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+static struct CE_attr host_ce_config_wlan_epping_irq[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 2048, 0, NULL,},
+ /* target->host EP-ping */
+ { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* target->host EP-ping */
+ { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* host->target EP-ping */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* host->target EP-ping */
+ { /* CE4 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* unused */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+/*
+ * EP-ping firmware's CE configuration
+ */
+static struct CE_pipe_config target_ce_config_wlan_epping[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ 0, PIPEDIR_OUT, 16, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host EP-ping */
+ { /* CE1 */ 1, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host EP-ping */
+ { /* CE2 */ 2, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target EP-ping */
+ { /* CE3 */ 3, PIPEDIR_OUT, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target EP-ping */
+ { /* CE4 */ 4, PIPEDIR_OUT, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ 5, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* unused */
+ { /* CE6 */ 6, PIPEDIR_INOUT, 0, 0, CE_ATTR_FLAGS, 0,},
+ /* CE7 used only by Host */
+ { /* CE7 */ 7, PIPEDIR_INOUT_H2H, 0, 0, 0, 0,},
+ /* CE8 used only by IPA */
+ { /* CE8 */ 8, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}
+};
+#else
+static struct CE_attr host_ce_config_wlan[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+ /* target->host HTT + HTC control */
+ { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 2048, 512, NULL,},
+ /* target->host WMI */
+ { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 32, NULL,},
+ /* host->target WMI */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 32, 2048, 0, NULL,},
+ /* host->target HTT */
+ { /* CE4 */ CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR, 0,
+ CE_HTT_H2T_MSG_SRC_NENTRIES, 256, 0, NULL,},
+ /* ipa_uc->target HTC control */
+ { /* CE5 */ CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR, 0,
+ 1024, 512, 0, NULL,},
+ /* Target autonomous HIF_memcpy */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ CE_ATTR_FLAGS | CE_ATTR_DISABLE_INTR,
+ 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+
+static struct CE_pipe_config target_ce_config_wlan[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ 0, PIPEDIR_OUT, 32, 256, CE_ATTR_FLAGS, 0,},
+ /* target->host HTT + HTC control */
+ { /* CE1 */ 1, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host WMI */
+ { /* CE2 */ 2, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target WMI */
+ { /* CE3 */ 3, PIPEDIR_OUT, 32, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target HTT */
+ { /* CE4 */ 4, PIPEDIR_OUT, 256, 256, CE_ATTR_FLAGS, 0,},
+ /* NB: 50% of src nentries, since tx has 2 frags */
+ /* ipa_uc->target HTC control */
+ { /* CE5 */ 5, PIPEDIR_OUT, 1024, 64, CE_ATTR_FLAGS, 0,},
+ /* Reserved for target autonomous HIF_memcpy */
+ { /* CE6 */ 6, PIPEDIR_INOUT, 32, 4096, CE_ATTR_FLAGS, 0,},
+ /* CE7 used only by Host */
+ { /* CE7 */ 7, PIPEDIR_INOUT_H2H, 0, 0, 0, 0,},
+ /* CE8 used only by IPA */
+ { /* CE8 */ 8, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}
+};
+
+static struct CE_attr host_ce_config_wlan_epping_poll[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+ /* target->host EP-ping */
+ { /* CE1 */ EPPING_CE_FLAGS_POLL, 0, 0, 2048, 128, NULL,},
+ /* target->host EP-ping */
+ { /* CE2 */ EPPING_CE_FLAGS_POLL, 0, 0, 2048, 128, NULL,},
+ /* host->target EP-ping */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* host->target EP-ping */
+ { /* CE4 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* unused */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+static struct CE_attr host_ce_config_wlan_epping_irq[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ CE_ATTR_FLAGS, 0, 16, 256, 0, NULL,},
+ /* target->host EP-ping */
+ { /* CE1 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* target->host EP-ping */
+ { /* CE2 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* host->target EP-ping */
+ { /* CE3 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* host->target EP-ping */
+ { /* CE4 */ CE_ATTR_FLAGS, 0, 128, 2048, 0, NULL,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ CE_ATTR_FLAGS, 0, 0, 2048, 128, NULL,},
+ /* unused */
+ { /* CE6 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,},
+ /* ce_diag, the Diagnostic Window */
+ { /* CE7 */ CE_ATTR_FLAGS, 0, 2, DIAG_TRANSFER_LIMIT, 2, NULL,},
+};
+/*
+ * EP-ping firmware's CE configuration
+ */
+static struct CE_pipe_config target_ce_config_wlan_epping[] = {
+ /* host->target HTC control and raw streams */
+ { /* CE0 */ 0, PIPEDIR_OUT, 16, 256, CE_ATTR_FLAGS, 0,},
+ /* target->host EP-ping */
+ { /* CE1 */ 1, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* target->host EP-ping */
+ { /* CE2 */ 2, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target EP-ping */
+ { /* CE3 */ 3, PIPEDIR_OUT, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* host->target EP-ping */
+ { /* CE4 */ 4, PIPEDIR_OUT, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* EP-ping heartbeat */
+ { /* CE5 */ 5, PIPEDIR_IN, 128, 2048, CE_ATTR_FLAGS, 0,},
+ /* unused */
+ { /* CE6 */ 6, PIPEDIR_INOUT, 0, 0, CE_ATTR_FLAGS, 0,},
+ /* CE7 used only by Host */
+ { /* CE7 */ 7, PIPEDIR_INOUT_H2H, 0, 0, 0, 0,},
+ /* CE8 used only by IPA */
+ { /* CE8 */ 8, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}
+};
+#endif
+
+static struct CE_attr *host_ce_config = host_ce_config_wlan;
+static struct CE_pipe_config *target_ce_config = target_ce_config_wlan;
+static int target_ce_config_sz = sizeof(target_ce_config_wlan);
+#endif /* __HIF_PCI_INTERNAL_H__ */
diff --git a/core/hif/src/ce/ce_bmi.c b/core/hif/src/ce/ce_bmi.c
new file mode 100644
index 0000000..b2d541d
--- /dev/null
+++ b/core/hif/src/ce/ce_bmi.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 2015 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 "targcfg.h"
+#include "cdf_lock.h"
+#include "cdf_status.h"
+#include <cdf_atomic.h> /* cdf_atomic_read */
+#include <targaddrs.h>
+#include <bmi_msg.h>
+#include "hif_io32.h"
+#include <hif.h>
+#include "regtable.h"
+#define ATH_MODULE_NAME hif
+#include <a_debug.h>
+#include "hif_main.h"
+#include "ce_api.h"
+#include "cdf_trace.h"
+#include "cds_api.h"
+#ifdef CONFIG_CNSS
+#include <net/cnss.h>
+#else
+#include "cnss_stub.h"
+#endif
+#include <cds_get_bin.h>
+#include "epping_main.h"
+#include "hif_debug.h"
+
+/* Track a BMI transaction that is in progress */
+#ifndef BIT
+#define BIT(n) (1 << (n))
+#endif
+
+enum {
+ BMI_REQ_SEND_DONE = BIT(0), /* the bmi tx completion */
+ BMI_RESP_RECV_DONE = BIT(1), /* the bmi respond is received */
+};
+
+struct BMI_transaction {
+ struct HIF_CE_state *hif_state;
+ cdf_semaphore_t bmi_transaction_sem;
+ uint8_t *bmi_request_host; /* Req BMI msg in Host addr space */
+ cdf_dma_addr_t bmi_request_CE; /* Req BMI msg in CE addr space */
+ uint32_t bmi_request_length; /* Length of BMI request */
+ uint8_t *bmi_response_host; /* Rsp BMI msg in Host addr space */
+ cdf_dma_addr_t bmi_response_CE; /* Rsp BMI msg in CE addr space */
+ unsigned int bmi_response_length; /* Length of received response */
+ unsigned int bmi_timeout_ms;
+ uint32_t bmi_transaction_flags; /* flags for the transcation */
+};
+
+/*
+ * send/recv completion functions for BMI.
+ * NB: The "net_buf" parameter is actually just a
+ * straight buffer, not an sk_buff.
+ */
+void hif_bmi_send_done(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t data,
+ unsigned int nbytes,
+ unsigned int transfer_id, unsigned int sw_index,
+ unsigned int hw_index, uint32_t toeplitz_hash_result)
+{
+ struct BMI_transaction *transaction =
+ (struct BMI_transaction *)transfer_context;
+ struct ol_softc *scn = transaction->hif_state->scn;
+
+#ifdef BMI_RSP_POLLING
+ /*
+ * Fix EV118783, Release a semaphore after sending
+ * no matter whether a response is been expecting now.
+ */
+ cdf_semaphore_release(scn->cdf_dev,
+ &transaction->bmi_transaction_sem);
+#else
+ /*
+ * If a response is anticipated, we'll complete the
+ * transaction if the response has been received.
+ * If no response is anticipated, complete the
+ * transaction now.
+ */
+ transaction->bmi_transaction_flags |= BMI_REQ_SEND_DONE;
+
+ /* resp is't needed or has already been received,
+ * never assume resp comes later then this */
+ if (!transaction->bmi_response_CE ||
+ (transaction->bmi_transaction_flags & BMI_RESP_RECV_DONE)) {
+ cdf_semaphore_release(scn->cdf_dev,
+ &transaction->bmi_transaction_sem);
+ }
+#endif
+}
+
+#ifndef BMI_RSP_POLLING
+void hif_bmi_recv_data(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t data,
+ unsigned int nbytes,
+ unsigned int transfer_id, unsigned int flags)
+{
+ struct BMI_transaction *transaction =
+ (struct BMI_transaction *)transfer_context;
+ struct ol_softc *scn = transaction->hif_state->scn;
+
+ transaction->bmi_response_length = nbytes;
+ transaction->bmi_transaction_flags |= BMI_RESP_RECV_DONE;
+
+ /* when both send/recv are done, the sem can be released */
+ if (transaction->bmi_transaction_flags & BMI_REQ_SEND_DONE) {
+ cdf_semaphore_release(scn->cdf_dev,
+ &transaction->bmi_transaction_sem);
+ }
+}
+#endif
+
+CDF_STATUS hif_exchange_bmi_msg(struct ol_softc *scn,
+ uint8_t *bmi_request,
+ uint32_t request_length,
+ uint8_t *bmi_response,
+ uint32_t *bmi_response_lengthp, uint32_t TimeoutMS)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ struct HIF_CE_pipe_info *send_pipe_info =
+ &(hif_state->pipe_info[BMI_CE_NUM_TO_TARG]);
+ struct CE_handle *ce_send_hdl = send_pipe_info->ce_hdl;
+ cdf_dma_addr_t CE_request, CE_response = 0;
+ struct BMI_transaction *transaction = NULL;
+ int status = CDF_STATUS_SUCCESS;
+ struct HIF_CE_pipe_info *recv_pipe_info =
+ &(hif_state->pipe_info[BMI_CE_NUM_TO_HOST]);
+ struct CE_handle *ce_recv = recv_pipe_info->ce_hdl;
+ unsigned int mux_id = 0;
+ unsigned int transaction_id = 0xffff;
+ unsigned int user_flags = 0;
+#ifdef BMI_RSP_POLLING
+ cdf_dma_addr_t buf;
+ unsigned int completed_nbytes, id, flags;
+ int i;
+#endif
+
+ transaction =
+ (struct BMI_transaction *)cdf_mem_malloc(sizeof(*transaction));
+ if (unlikely(!transaction)) {
+ HIF_ERROR("%s: no memory", __func__);
+ return CDF_STATUS_E_NOMEM;
+ }
+ transaction_id = (mux_id & MUX_ID_MASK) |
+ (transaction_id & TRANSACTION_ID_MASK);
+#ifdef QCA_WIFI_3_0
+ user_flags &= DESC_DATA_FLAG_MASK;
+#endif
+ A_TARGET_ACCESS_LIKELY(scn);
+
+ /* Initialize bmi_transaction_sem to block */
+ cdf_semaphore_init(&transaction->bmi_transaction_sem);
+ cdf_semaphore_acquire(scn->cdf_dev, &transaction->bmi_transaction_sem);
+
+ transaction->hif_state = hif_state;
+ transaction->bmi_request_host = bmi_request;
+ transaction->bmi_request_length = request_length;
+ transaction->bmi_response_length = 0;
+ transaction->bmi_timeout_ms = TimeoutMS;
+ transaction->bmi_transaction_flags = 0;
+
+ /*
+ * CE_request = dma_map_single(dev,
+ * (void *)bmi_request, request_length, DMA_TO_DEVICE);
+ */
+ CE_request = scn->bmi_cmd_da;
+ transaction->bmi_request_CE = CE_request;
+
+ if (bmi_response) {
+
+ /*
+ * CE_response = dma_map_single(dev, bmi_response,
+ * BMI_DATASZ_MAX, DMA_FROM_DEVICE);
+ */
+ CE_response = scn->bmi_rsp_da;
+ transaction->bmi_response_host = bmi_response;
+ transaction->bmi_response_CE = CE_response;
+ /* dma_cache_sync(dev, bmi_response,
+ BMI_DATASZ_MAX, DMA_FROM_DEVICE); */
+ cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev,
+ CE_response,
+ BMI_DATASZ_MAX,
+ DMA_FROM_DEVICE);
+ ce_recv_buf_enqueue(ce_recv, transaction,
+ transaction->bmi_response_CE);
+ /* NB: see HIF_BMI_recv_done */
+ } else {
+ transaction->bmi_response_host = NULL;
+ transaction->bmi_response_CE = 0;
+ }
+
+ /* dma_cache_sync(dev, bmi_request, request_length, DMA_TO_DEVICE); */
+ cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev, CE_request,
+ request_length, DMA_TO_DEVICE);
+
+ status =
+ ce_send(ce_send_hdl, transaction,
+ CE_request, request_length,
+ transaction_id, 0, user_flags);
+ ASSERT(status == CDF_STATUS_SUCCESS);
+ /* NB: see hif_bmi_send_done */
+
+ /* TBDXXX: handle timeout */
+
+ /* Wait for BMI request/response transaction to complete */
+ /* Always just wait for BMI request here if
+ * BMI_RSP_POLLING is defined */
+ while (cdf_semaphore_acquire
+ (scn->cdf_dev, &transaction->bmi_transaction_sem)) {
+ /*need some break out condition(time out?) */
+ }
+
+ if (bmi_response) {
+#ifdef BMI_RSP_POLLING
+ /* Fix EV118783, do not wait a semaphore for the BMI response
+ * since the relative interruption may be lost.
+ * poll the BMI response instead.
+ */
+ i = 0;
+ while (ce_completed_recv_next(
+ ce_recv, NULL, NULL, &buf,
+ &completed_nbytes, &id,
+ &flags) != CDF_STATUS_SUCCESS) {
+ if (i++ > BMI_RSP_TO_MILLISEC) {
+ HIF_ERROR("%s:error, can't get bmi response\n",
+ __func__);
+ status = CDF_STATUS_E_BUSY;
+ break;
+ }
+ OS_DELAY(1000);
+ }
+
+ if ((status == CDF_STATUS_SUCCESS) && bmi_response_lengthp)
+ *bmi_response_lengthp = completed_nbytes;
+#else
+ if ((status == CDF_STATUS_SUCCESS) && bmi_response_lengthp) {
+ *bmi_response_lengthp =
+ transaction->bmi_response_length;
+ }
+#endif
+
+ }
+
+ /* dma_unmap_single(dev, transaction->bmi_request_CE,
+ request_length, DMA_TO_DEVICE); */
+ /* bus_unmap_single(scn->sc_osdev,
+ transaction->bmi_request_CE,
+ request_length, BUS_DMA_TODEVICE); */
+
+ if (status != CDF_STATUS_SUCCESS) {
+ cdf_dma_addr_t unused_buffer;
+ unsigned int unused_nbytes;
+ unsigned int unused_id;
+ unsigned int toeplitz_hash_result;
+
+ ce_cancel_send_next(ce_send_hdl,
+ NULL, NULL, &unused_buffer,
+ &unused_nbytes, &unused_id,
+ &toeplitz_hash_result);
+ }
+
+ A_TARGET_ACCESS_UNLIKELY(scn);
+ cdf_mem_free(transaction);
+ return status;
+}
diff --git a/core/hif/src/ce/ce_bmi.h b/core/hif/src/ce/ce_bmi.h
new file mode 100644
index 0000000..c09b211
--- /dev/null
+++ b/core/hif/src/ce/ce_bmi.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef __CE_BMI_H__
+#define __CE_BMI_H__
+
+#include <cdf_atomic.h> /* cdf_atomic_read */
+#include "cdf_lock.h"
+#include "ce_api.h"
+#include "cepci.h"
+
+void hif_bmi_recv_data(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t data,
+ unsigned int nbytes,
+ unsigned int transfer_id, unsigned int flags);
+void hif_bmi_send_done(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t data,
+ unsigned int nbytes,
+ unsigned int transfer_id, unsigned int sw_index,
+ unsigned int hw_index, uint32_t toeplitz_hash_result);
+#endif /* __CE_BMI_H__ */
diff --git a/core/hif/src/ce/ce_diag.c b/core/hif/src/ce/ce_diag.c
new file mode 100644
index 0000000..c078f21
--- /dev/null
+++ b/core/hif/src/ce/ce_diag.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2015 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 "targcfg.h"
+#include "cdf_lock.h"
+#include "cdf_status.h"
+#include <cdf_atomic.h> /* cdf_atomic_read */
+#include <targaddrs.h>
+#include <bmi_msg.h>
+#include "hif_io32.h"
+#include <hif.h>
+#include <htc_services.h>
+#include "regtable.h"
+#include <a_debug.h>
+#include "hif_main.h"
+#include "ce_api.h"
+#include "cdf_trace.h"
+#include "cds_api.h"
+#ifdef CONFIG_CNSS
+#include <net/cnss.h>
+#endif
+#include <cds_get_bin.h>
+#include "hif_debug.h"
+#include "epping_main.h"
+
+void hif_dump_target_memory(struct ol_softc *scn, void *ramdump_base,
+ uint32_t address, uint32_t size)
+{
+ uint32_t loc = address;
+ uint32_t val = 0;
+ uint32_t j = 0;
+ u8 *temp = ramdump_base;
+
+ A_TARGET_ACCESS_BEGIN(scn);
+ while (j < size) {
+ val = hif_read32_mb(scn->mem + loc + j);
+ cdf_mem_copy(temp, &val, 4);
+ j += 4;
+ temp += 4;
+ }
+ A_TARGET_ACCESS_END(scn);
+}
+/*
+ * TBDXXX: Should be a function call specific to each Target-type.
+ * This convoluted macro converts from Target CPU Virtual Address
+ * Space to CE Address Space. As part of this process, we
+ * conservatively fetch the current PCIE_BAR. MOST of the time,
+ * this should match the upper bits of PCI space for this device;
+ * but that's not guaranteed.
+ */
+#ifdef QCA_WIFI_3_0
+#define TARG_CPU_SPACE_TO_CE_SPACE(pci_addr, addr) \
+ (scn->mem_pa + addr)
+#else
+#define TARG_CPU_SPACE_TO_CE_SPACE(pci_addr, addr) \
+ (((hif_read32_mb((pci_addr) + \
+ (SOC_CORE_BASE_ADDRESS|CORE_CTRL_ADDRESS)) & 0x7ff) << 21) \
+ | 0x100000 | ((addr) & 0xfffff))
+#endif
+/* Wait up to this many Ms for a Diagnostic Access CE operation to complete */
+#define DIAG_ACCESS_CE_TIMEOUT_MS 10
+
+/*
+ * Diagnostic read/write access is provided for startup/config/debug usage.
+ * Caller must guarantee proper alignment, when applicable, and single user
+ * at any moment.
+ */
+
+CDF_STATUS
+hif_diag_read_mem(struct ol_softc *scn, uint32_t address, uint8_t *data,
+ int nbytes)
+{
+ struct HIF_CE_state *hif_state;
+ CDF_STATUS status = CDF_STATUS_SUCCESS;
+ cdf_dma_addr_t buf;
+ unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+ unsigned int id;
+ unsigned int flags;
+ struct CE_handle *ce_diag;
+ cdf_dma_addr_t CE_data; /* Host buffer address in CE space */
+ cdf_dma_addr_t CE_data_base = 0;
+ void *data_buf = NULL;
+ int i;
+ unsigned int mux_id = 0;
+ unsigned int transaction_id = 0xffff;
+ cdf_dma_addr_t ce_phy_addr = address;
+ unsigned int toeplitz_hash_result;
+ unsigned int user_flags = 0;
+
+ hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ transaction_id = (mux_id & MUX_ID_MASK) |
+ (transaction_id & TRANSACTION_ID_MASK);
+#ifdef QCA_WIFI_3_0
+ user_flags &= DESC_DATA_FLAG_MASK;
+#endif
+
+ /* This code cannot handle reads to non-memory space. Redirect to the
+ * register read fn but preserve the multi word read capability of
+ * this fn
+ */
+ if (address < DRAM_BASE_ADDRESS) {
+
+ if ((address & 0x3) || ((uintptr_t) data & 0x3))
+ return CDF_STATUS_E_INVAL;
+
+ while ((nbytes >= 4) &&
+ (CDF_STATUS_SUCCESS == (status =
+ hif_diag_read_access(scn, address,
+ (uint32_t *)data)))) {
+
+ nbytes -= sizeof(uint32_t);
+ address += sizeof(uint32_t);
+ data += sizeof(uint32_t);
+
+ }
+
+ return status;
+ }
+ ce_diag = hif_state->ce_diag;
+
+ A_TARGET_ACCESS_LIKELY(scn);
+
+ /*
+ * Allocate a temporary bounce buffer to hold caller's data
+ * to be DMA'ed from Target. This guarantees
+ * 1) 4-byte alignment
+ * 2) Buffer in DMA-able space
+ */
+ orig_nbytes = nbytes;
+ data_buf = cdf_os_mem_alloc_consistent(scn->cdf_dev,
+ orig_nbytes, &CE_data_base, 0);
+ if (!data_buf) {
+ status = CDF_STATUS_E_NOMEM;
+ goto done;
+ }
+ cdf_mem_set(data_buf, orig_nbytes, 0);
+ cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev, CE_data_base,
+ orig_nbytes, DMA_FROM_DEVICE);
+
+ remaining_bytes = orig_nbytes;
+ CE_data = CE_data_base;
+ while (remaining_bytes) {
+ nbytes = min(remaining_bytes, DIAG_TRANSFER_LIMIT);
+ {
+ status = ce_recv_buf_enqueue(ce_diag, NULL, CE_data);
+ if (status != CDF_STATUS_SUCCESS)
+ goto done;
+ }
+
+ { /* Request CE to send from Target(!)
+ * address to Host buffer */
+ /*
+ * The address supplied by the caller is in the
+ * Target CPU virtual address space.
+ *
+ * In order to use this address with the diagnostic CE,
+ * convert it from
+ * Target CPU virtual address space
+ * to
+ * CE address space
+ */
+ A_TARGET_ACCESS_BEGIN_RET(scn);
+ ce_phy_addr =
+ TARG_CPU_SPACE_TO_CE_SPACE(scn->mem, address);
+ A_TARGET_ACCESS_END_RET(scn);
+
+ status =
+ ce_send(ce_diag, NULL, ce_phy_addr, nbytes,
+ transaction_id, 0, user_flags);
+ if (status != CDF_STATUS_SUCCESS)
+ goto done;
+ }
+
+ i = 0;
+ while (ce_completed_send_next(ce_diag, NULL, NULL, &buf,
+ &completed_nbytes, &id, NULL, NULL,
+ &toeplitz_hash_result) != CDF_STATUS_SUCCESS) {
+ cdf_mdelay(1);
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+ status = CDF_STATUS_E_BUSY;
+ goto done;
+ }
+ }
+ if (nbytes != completed_nbytes) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+ if (buf != ce_phy_addr) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ i = 0;
+ while (ce_completed_recv_next
+ (ce_diag, NULL, NULL, &buf,
+ &completed_nbytes, &id,
+ &flags) != CDF_STATUS_SUCCESS) {
+ cdf_mdelay(1);
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+ status = CDF_STATUS_E_BUSY;
+ goto done;
+ }
+ }
+ if (nbytes != completed_nbytes) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+ if (buf != CE_data) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ remaining_bytes -= nbytes;
+ address += nbytes;
+ CE_data += nbytes;
+ }
+
+done:
+ A_TARGET_ACCESS_UNLIKELY(scn);
+
+ if (status == CDF_STATUS_SUCCESS)
+ cdf_mem_copy(data, data_buf, orig_nbytes);
+ else
+ HIF_ERROR("%s failure (0x%x)", __func__, address);
+
+ if (data_buf)
+ cdf_os_mem_free_consistent(scn->cdf_dev, orig_nbytes,
+ data_buf, CE_data_base, 0);
+
+ return status;
+}
+
+/* Read 4-byte aligned data from Target memory or register */
+CDF_STATUS hif_diag_read_access(struct ol_softc *scn,
+ uint32_t address, uint32_t *data)
+{
+ struct HIF_CE_state *hif_state;
+
+ hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ if (address >= DRAM_BASE_ADDRESS) {
+ /* Assume range doesn't cross this boundary */
+ return hif_diag_read_mem(scn, address, (uint8_t *) data,
+ sizeof(uint32_t));
+ } else {
+ A_TARGET_ACCESS_BEGIN_RET(scn);
+ *data = A_TARGET_READ(scn, address);
+ A_TARGET_ACCESS_END_RET(scn);
+
+ return CDF_STATUS_SUCCESS;
+ }
+}
+
+CDF_STATUS hif_diag_write_mem(struct ol_softc *scn,
+ uint32_t address, uint8_t *data, int nbytes)
+{
+ struct HIF_CE_state *hif_state;
+ CDF_STATUS status = CDF_STATUS_SUCCESS;
+ cdf_dma_addr_t buf;
+ unsigned int completed_nbytes, orig_nbytes, remaining_bytes;
+ unsigned int id;
+ unsigned int flags;
+ struct CE_handle *ce_diag;
+ void *data_buf = NULL;
+ cdf_dma_addr_t CE_data; /* Host buffer address in CE space */
+ cdf_dma_addr_t CE_data_base = 0;
+ int i;
+ unsigned int mux_id = 0;
+ unsigned int transaction_id = 0xffff;
+ cdf_dma_addr_t ce_phy_addr = address;
+ unsigned int toeplitz_hash_result;
+ unsigned int user_flags = 0;
+
+ hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ ce_diag = hif_state->ce_diag;
+ transaction_id = (mux_id & MUX_ID_MASK) |
+ (transaction_id & TRANSACTION_ID_MASK);
+#ifdef QCA_WIFI_3_0
+ user_flags &= DESC_DATA_FLAG_MASK;
+#endif
+
+ A_TARGET_ACCESS_LIKELY(scn);
+
+ /*
+ * Allocate a temporary bounce buffer to hold caller's data
+ * to be DMA'ed to Target. This guarantees
+ * 1) 4-byte alignment
+ * 2) Buffer in DMA-able space
+ */
+ orig_nbytes = nbytes;
+ data_buf = cdf_os_mem_alloc_consistent(scn->cdf_dev,
+ orig_nbytes, &CE_data_base, 0);
+ if (!data_buf) {
+ status = A_NO_MEMORY;
+ goto done;
+ }
+
+ /* Copy caller's data to allocated DMA buf */
+ cdf_mem_copy(data_buf, data, orig_nbytes);
+ cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev, CE_data_base,
+ orig_nbytes, DMA_TO_DEVICE);
+
+ /*
+ * The address supplied by the caller is in the
+ * Target CPU virtual address space.
+ *
+ * In order to use this address with the diagnostic CE,
+ * convert it from
+ * Target CPU virtual address space
+ * to
+ * CE address space
+ */
+ A_TARGET_ACCESS_BEGIN_RET(scn);
+ ce_phy_addr = TARG_CPU_SPACE_TO_CE_SPACE(scn->mem, address);
+ A_TARGET_ACCESS_END_RET(scn);
+
+ remaining_bytes = orig_nbytes;
+ CE_data = CE_data_base;
+ while (remaining_bytes) {
+ nbytes = min(remaining_bytes, DIAG_TRANSFER_LIMIT);
+
+ { /* Set up to receive directly into Target(!) address */
+ status = ce_recv_buf_enqueue(ce_diag,
+ NULL, ce_phy_addr);
+ if (status != CDF_STATUS_SUCCESS)
+ goto done;
+ }
+
+ {
+ /*
+ * Request CE to send caller-supplied data that
+ * was copied to bounce buffer to Target(!) address.
+ */
+ status =
+ ce_send(ce_diag, NULL,
+ (cdf_dma_addr_t) CE_data, nbytes,
+ transaction_id, 0, user_flags);
+ if (status != CDF_STATUS_SUCCESS)
+ goto done;
+ }
+
+ i = 0;
+ while (ce_completed_send_next(ce_diag, NULL, NULL, &buf,
+ &completed_nbytes, &id,
+ NULL, NULL, &toeplitz_hash_result) !=
+ CDF_STATUS_SUCCESS) {
+ cdf_mdelay(1);
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+ status = CDF_STATUS_E_BUSY;
+ goto done;
+ }
+ }
+
+ if (nbytes != completed_nbytes) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ if (buf != CE_data) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ i = 0;
+ while (ce_completed_recv_next
+ (ce_diag, NULL, NULL, &buf,
+ &completed_nbytes, &id,
+ &flags) != CDF_STATUS_SUCCESS) {
+ cdf_mdelay(1);
+ if (i++ > DIAG_ACCESS_CE_TIMEOUT_MS) {
+ status = CDF_STATUS_E_BUSY;
+ goto done;
+ }
+ }
+
+ if (nbytes != completed_nbytes) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ if (buf != ce_phy_addr) {
+ status = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ remaining_bytes -= nbytes;
+ address += nbytes;
+ CE_data += nbytes;
+ }
+
+done:
+ A_TARGET_ACCESS_UNLIKELY(scn);
+
+ if (data_buf) {
+ cdf_os_mem_free_consistent(scn->cdf_dev, orig_nbytes,
+ data_buf, CE_data_base, 0);
+ }
+
+ if (status != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s failure (0x%llu)", __func__,
+ (uint64_t)ce_phy_addr);
+ }
+
+ return status;
+}
+
+/* Write 4B data to Target memory or register */
+CDF_STATUS
+hif_diag_write_access(struct ol_softc *scn, uint32_t address, uint32_t data)
+{
+ struct HIF_CE_state *hif_state;
+
+ hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ if (address >= DRAM_BASE_ADDRESS) {
+ /* Assume range doesn't cross this boundary */
+ uint32_t data_buf = data;
+
+ return hif_diag_write_mem(scn, address,
+ (uint8_t *) &data_buf,
+ sizeof(uint32_t));
+ } else {
+ A_TARGET_ACCESS_BEGIN_RET(scn);
+ A_TARGET_WRITE(scn, address, data);
+ A_TARGET_ACCESS_END_RET(scn);
+
+ return CDF_STATUS_SUCCESS;
+ }
+}
diff --git a/core/hif/src/ce/ce_internal.h b/core/hif/src/ce/ce_internal.h
new file mode 100644
index 0000000..b3fa707
--- /dev/null
+++ b/core/hif/src/ce/ce_internal.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2013-2015 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.
+ */
+#ifndef __COPY_ENGINE_INTERNAL_H__
+#define __COPY_ENGINE_INTERNAL_H__
+
+#include <hif.h> /* A_TARGET_WRITE */
+
+/* Copy Engine operational state */
+enum CE_op_state {
+ CE_UNUSED,
+ CE_PAUSED,
+ CE_RUNNING,
+};
+
+enum ol_ath_hif_ce_ecodes {
+ CE_RING_DELTA_FAIL = 0
+};
+
+struct CE_src_desc;
+
+/* Copy Engine Ring internal state */
+struct CE_ring_state {
+
+ /* Number of entries in this ring; must be power of 2 */
+ unsigned int nentries;
+ unsigned int nentries_mask;
+
+ /*
+ * For dest ring, this is the next index to be processed
+ * by software after it was/is received into.
+ *
+ * For src ring, this is the last descriptor that was sent
+ * and completion processed by software.
+ *
+ * Regardless of src or dest ring, this is an invariant
+ * (modulo ring size):
+ * write index >= read index >= sw_index
+ */
+ unsigned int sw_index;
+ unsigned int write_index; /* cached copy */
+ /*
+ * For src ring, this is the next index not yet processed by HW.
+ * This is a cached copy of the real HW index (read index), used
+ * for avoiding reading the HW index register more often than
+ * necessary.
+ * This extends the invariant:
+ * write index >= read index >= hw_index >= sw_index
+ *
+ * For dest ring, this is currently unused.
+ */
+ unsigned int hw_index; /* cached copy */
+
+ /* Start of DMA-coherent area reserved for descriptors */
+ void *base_addr_owner_space_unaligned; /* Host address space */
+ cdf_dma_addr_t base_addr_CE_space_unaligned; /* CE address space */
+
+ /*
+ * Actual start of descriptors.
+ * Aligned to descriptor-size boundary.
+ * Points into reserved DMA-coherent area, above.
+ */
+ void *base_addr_owner_space; /* Host address space */
+ cdf_dma_addr_t base_addr_CE_space; /* CE address space */
+ /*
+ * Start of shadow copy of descriptors, within regular memory.
+ * Aligned to descriptor-size boundary.
+ */
+ char *shadow_base_unaligned;
+ struct CE_src_desc *shadow_base;
+
+ unsigned int low_water_mark_nentries;
+ unsigned int high_water_mark_nentries;
+ void **per_transfer_context;
+ OS_DMA_MEM_CONTEXT(ce_dmacontext) /* OS Specific DMA context */
+};
+
+/* Copy Engine internal state */
+struct CE_state {
+ struct ol_softc *scn;
+ unsigned int id;
+ unsigned int attr_flags; /* CE_ATTR_* */
+ uint32_t ctrl_addr; /* relative to BAR */
+ enum CE_op_state state;
+
+#ifdef WLAN_FEATURE_FASTPATH
+ u_int32_t download_len; /* pkt download length for source ring */
+#endif /* WLAN_FEATURE_FASTPATH */
+
+ ce_send_cb send_cb;
+ void *send_context;
+
+ CE_recv_cb recv_cb;
+ void *recv_context;
+
+ /* misc_cbs - are any callbacks besides send and recv enabled? */
+ uint8_t misc_cbs;
+
+ CE_watermark_cb watermark_cb;
+ void *wm_context;
+
+ /*Record the state of the copy compl interrupt */
+ int disable_copy_compl_intr;
+
+ unsigned int src_sz_max;
+ struct CE_ring_state *src_ring;
+ struct CE_ring_state *dest_ring;
+ atomic_t rx_pending;
+
+ /* epping */
+ bool timer_inited;
+ cdf_softirq_timer_t poll_timer;
+ void (*lro_flush_cb)(void *);
+ void *lro_data;
+};
+
+/* Descriptor rings must be aligned to this boundary */
+#define CE_DESC_RING_ALIGN 8
+
+#ifdef QCA_WIFI_3_0
+#define HIF_CE_DESC_ADDR_TO_DMA(desc) \
+ (cdf_dma_addr_t)(((uint64_t)(desc)->buffer_addr + \
+ ((uint64_t)((desc)->buffer_addr_hi & 0x1F) << 32)))
+#else
+#define HIF_CE_DESC_ADDR_TO_DMA(desc) \
+ (cdf_dma_addr_t)((desc)->buffer_addr)
+#endif
+
+#ifdef QCA_WIFI_3_0
+struct CE_src_desc {
+ uint32_t buffer_addr:32;
+#if _BYTE_ORDER == _BIG_ENDIAN
+ uint32_t gather:1,
+ enable_11h:1,
+ meta_data_low:2, /* fw_metadata_low */
+ packet_result_offset:12,
+ toeplitz_hash_enable:1,
+ addr_y_search_disable:1,
+ addr_x_search_disable:1,
+ misc_int_disable:1,
+ target_int_disable:1,
+ host_int_disable:1,
+ dest_byte_swap:1,
+ byte_swap:1,
+ type:2,
+ tx_classify:1,
+ buffer_addr_hi:5;
+ uint32_t meta_data:16, /* fw_metadata_high */
+ nbytes:16; /* length in register map */
+#else
+ uint32_t buffer_addr_hi:5,
+ tx_classify:1,
+ type:2,
+ byte_swap:1, /* src_byte_swap */
+ dest_byte_swap:1,
+ host_int_disable:1,
+ target_int_disable:1,
+ misc_int_disable:1,
+ addr_x_search_disable:1,
+ addr_y_search_disable:1,
+ toeplitz_hash_enable:1,
+ packet_result_offset:12,
+ meta_data_low:2, /* fw_metadata_low */
+ enable_11h:1,
+ gather:1;
+ uint32_t nbytes:16, /* length in register map */
+ meta_data:16; /* fw_metadata_high */
+#endif
+ uint32_t toeplitz_hash_result:32;
+};
+
+struct CE_dest_desc {
+ uint32_t buffer_addr:32;
+#if _BYTE_ORDER == _BIG_ENDIAN
+ uint32_t gather:1,
+ enable_11h:1,
+ meta_data_low:2, /* fw_metadata_low */
+ packet_result_offset:12,
+ toeplitz_hash_enable:1,
+ addr_y_search_disable:1,
+ addr_x_search_disable:1,
+ misc_int_disable:1,
+ target_int_disable:1,
+ host_int_disable:1,
+ byte_swap:1,
+ src_byte_swap:1,
+ type:2,
+ tx_classify:1,
+ buffer_addr_hi:5;
+ uint32_t meta_data:16, /* fw_metadata_high */
+ nbytes:16; /* length in register map */
+#else
+ uint32_t buffer_addr_hi:5,
+ tx_classify:1,
+ type:2,
+ src_byte_swap:1,
+ byte_swap:1, /* dest_byte_swap */
+ host_int_disable:1,
+ target_int_disable:1,
+ misc_int_disable:1,
+ addr_x_search_disable:1,
+ addr_y_search_disable:1,
+ toeplitz_hash_enable:1,
+ packet_result_offset:12,
+ meta_data_low:2, /* fw_metadata_low */
+ enable_11h:1,
+ gather:1;
+ uint32_t nbytes:16, /* length in register map */
+ meta_data:16; /* fw_metadata_high */
+#endif
+ uint32_t toeplitz_hash_result:32;
+};
+#else
+struct CE_src_desc {
+ uint32_t buffer_addr;
+#if _BYTE_ORDER == _BIG_ENDIAN
+ uint32_t meta_data:14,
+ byte_swap:1,
+ gather:1,
+ nbytes:16;
+#else
+
+ uint32_t nbytes:16,
+ gather:1,
+ byte_swap:1,
+ meta_data:14;
+#endif
+};
+
+struct CE_dest_desc {
+ uint32_t buffer_addr;
+#if _BYTE_ORDER == _BIG_ENDIAN
+ uint32_t meta_data:14,
+ byte_swap:1,
+ gather:1,
+ nbytes:16;
+#else
+ uint32_t nbytes:16,
+ gather:1,
+ byte_swap:1,
+ meta_data:14;
+#endif
+};
+#endif /* QCA_WIFI_3_0 */
+
+#define CE_SENDLIST_ITEMS_MAX 12
+
+enum ce_sendlist_type_e {
+ CE_SIMPLE_BUFFER_TYPE,
+ /* TBDXXX: CE_RX_DESC_LIST, */
+};
+
+/*
+ * There's a public "ce_sendlist" and a private "ce_sendlist_s".
+ * The former is an opaque structure with sufficient space
+ * to hold the latter. The latter is the actual structure
+ * definition and it is only used internally. The opaque version
+ * of the structure allows callers to allocate an instance on the
+ * run-time stack without knowing any of the details of the
+ * structure layout.
+ */
+struct ce_sendlist_s {
+ unsigned int num_items;
+ struct ce_sendlist_item {
+ enum ce_sendlist_type_e send_type;
+ dma_addr_t data; /* e.g. buffer or desc list */
+ union {
+ unsigned int nbytes; /* simple buffer */
+ unsigned int ndesc; /* Rx descriptor list */
+ } u;
+ /* flags: externally-specified flags;
+ * OR-ed with internal flags */
+ uint32_t flags;
+ uint32_t user_flags;
+ } item[CE_SENDLIST_ITEMS_MAX];
+};
+
+#ifdef WLAN_FEATURE_FASTPATH
+void ce_h2t_tx_ce_cleanup(struct CE_handle *ce_hdl);
+#endif
+
+/* which ring of a CE? */
+#define CE_RING_SRC 0
+#define CE_RING_DEST 1
+
+#define CDC_WAR_MAGIC_STR 0xceef0000
+#define CDC_WAR_DATA_CE 4
+
+/* Additional internal-only ce_send flags */
+#define CE_SEND_FLAG_GATHER 0x00010000 /* Use Gather */
+#endif /* __COPY_ENGINE_INTERNAL_H__ */
diff --git a/core/hif/src/ce/ce_main.c b/core/hif/src/ce/ce_main.c
new file mode 100644
index 0000000..8cf00f1
--- /dev/null
+++ b/core/hif/src/ce/ce_main.c
@@ -0,0 +1,2593 @@
+/*
+ * Copyright (c) 2013-2015 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 "targcfg.h"
+#include "cdf_lock.h"
+#include "cdf_status.h"
+#include <cdf_atomic.h> /* cdf_atomic_read */
+#include <targaddrs.h>
+#include <bmi_msg.h>
+#include "hif_io32.h"
+#include <hif.h>
+#include "regtable.h"
+#define ATH_MODULE_NAME hif
+#include <a_debug.h>
+#include "hif_main.h"
+#ifdef HIF_PCI
+#include "ce_bmi.h"
+#endif
+#include "ce_api.h"
+#include "cdf_trace.h"
+#include "cds_api.h"
+#ifdef CONFIG_CNSS
+#include <net/cnss.h>
+#endif
+#include <cds_get_bin.h>
+#include "epping_main.h"
+#include "hif_debug.h"
+#include "ce_internal.h"
+#include "ce_reg.h"
+#include "ce_assignment.h"
+#include "ce_tasklet.h"
+#ifdef HIF_PCI
+#include "icnss_stub.h"
+#else
+#include <soc/qcom/icnss.h>
+#endif
+#include "qwlan_version.h"
+
+#define CE_POLL_TIMEOUT 10 /* ms */
+
+/* Forward references */
+static int hif_post_recv_buffers_for_pipe(struct HIF_CE_pipe_info *pipe_info);
+
+/*
+ * Fix EV118783, poll to check whether a BMI response comes
+ * other than waiting for the interruption which may be lost.
+ */
+/* #define BMI_RSP_POLLING */
+#define BMI_RSP_TO_MILLISEC 1000
+
+
+static int hif_post_recv_buffers(struct ol_softc *scn);
+
+static void ce_poll_timeout(void *arg)
+{
+ struct CE_state *CE_state = (struct CE_state *)arg;
+ if (CE_state->timer_inited) {
+ ce_per_engine_service(CE_state->scn, CE_state->id);
+ cdf_softirq_timer_mod(&CE_state->poll_timer, CE_POLL_TIMEOUT);
+ }
+}
+
+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;
+ }
+
+ CDF_ASSERT(0); /* n too large */
+ return 0;
+}
+
+/*
+ * 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 ol_softc *scn,
+ unsigned int CE_id, struct CE_attr *attr)
+{
+ struct CE_state *CE_state;
+ uint32_t ctrl_addr;
+ unsigned int nentries;
+ cdf_dma_addr_t base_addr;
+ bool malloc_CE_state = false;
+ bool malloc_src_ring = false;
+
+ CDF_ASSERT(CE_id < scn->ce_count);
+ ctrl_addr = CE_BASE_ADDRESS(CE_id);
+ cdf_spin_lock(&scn->target_lock);
+ CE_state = scn->ce_id_to_state[CE_id];
+
+ if (!CE_state) {
+ cdf_spin_unlock(&scn->target_lock);
+ CE_state =
+ (struct CE_state *)cdf_mem_malloc(sizeof(*CE_state));
+ if (!CE_state) {
+ HIF_ERROR("%s: CE_state has no mem", __func__);
+ return NULL;
+ } else
+ malloc_CE_state = true;
+ cdf_mem_zero(CE_state, sizeof(*CE_state));
+ cdf_spin_lock(&scn->target_lock);
+ if (!scn->ce_id_to_state[CE_id]) { /* re-check under lock */
+ scn->ce_id_to_state[CE_id] = CE_state;
+
+ CE_state->id = CE_id;
+ CE_state->ctrl_addr = ctrl_addr;
+ CE_state->state = CE_RUNNING;
+ CE_state->attr_flags = attr->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).
+ */
+ cdf_mem_free(CE_state);
+ malloc_CE_state = false;
+ CE_state = scn->ce_id_to_state[CE_id];
+ }
+ }
+ CE_state->scn = scn;
+ cdf_spin_unlock(&scn->target_lock);
+
+ cdf_atomic_init(&CE_state->rx_pending);
+ if (attr == NULL) {
+ /* Already initialized; caller wants the handle */
+ return (struct CE_handle *)CE_state;
+ }
+
+#ifdef ADRASTEA_SHADOW_REGISTERS
+ HIF_ERROR("%s: Using Shadow Registers instead of CE Registers\n",
+ __func__);
+#endif
+
+ if (CE_state->src_sz_max)
+ CDF_ASSERT(CE_state->src_sz_max == attr->src_sz_max);
+ else
+ CE_state->src_sz_max = attr->src_sz_max;
+
+ /* source ring setup */
+ nentries = attr->src_nentries;
+ if (nentries) {
+ struct CE_ring_state *src_ring;
+ unsigned CE_nbytes;
+ char *ptr;
+ uint64_t dma_addr;
+ nentries = roundup_pwr2(nentries);
+ if (CE_state->src_ring) {
+ CDF_ASSERT(CE_state->src_ring->nentries == nentries);
+ } else {
+ CE_nbytes = sizeof(struct CE_ring_state)
+ + (nentries * sizeof(void *));
+ ptr = cdf_mem_malloc(CE_nbytes);
+ if (!ptr) {
+ /* cannot allocate src ring. If the
+ * CE_state is allocated locally free
+ * CE_State and return error.
+ */
+ HIF_ERROR("%s: src ring has no mem", __func__);
+ if (malloc_CE_state) {
+ /* allocated CE_state locally */
+ cdf_spin_lock(&scn->target_lock);
+ scn->ce_id_to_state[CE_id] = NULL;
+ cdf_spin_unlock(&scn->target_lock);
+ cdf_mem_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;
+ }
+ cdf_mem_zero(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(scn);
+ src_ring->hw_index =
+ CE_SRC_RING_READ_IDX_GET(scn, ctrl_addr);
+ src_ring->sw_index = src_ring->hw_index;
+ src_ring->write_index =
+ CE_SRC_RING_WRITE_IDX_GET(scn, ctrl_addr);
+ A_TARGET_ACCESS_END_RET_PTR(scn);
+ 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 =
+ cdf_os_mem_alloc_consistent(scn->cdf_dev,
+ (nentries *
+ sizeof(struct CE_src_desc) +
+ CE_DESC_RING_ALIGN),
+ &base_addr, 0);
+ if (src_ring->base_addr_owner_space_unaligned
+ == NULL) {
+ HIF_ERROR("%s: src ring has no DMA mem",
+ __func__);
+ 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 =
+ cdf_mem_malloc(nentries *
+ sizeof(struct CE_src_desc) +
+ CE_DESC_RING_ALIGN);
+ if (src_ring->shadow_base_unaligned == NULL) {
+ HIF_ERROR("%s: src ring no shadow_base mem",
+ __func__);
+ 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(scn);
+ dma_addr = src_ring->base_addr_CE_space;
+ CE_SRC_RING_BASE_ADDR_SET(scn, ctrl_addr,
+ (uint32_t)(dma_addr & 0xFFFFFFFF));
+#ifdef WLAN_ENABLE_QCA6180
+ {
+ uint32_t tmp;
+ tmp = CE_SRC_RING_BASE_ADDR_HIGH_GET(
+ scn, ctrl_addr);
+ tmp &= ~0x1F;
+ dma_addr = ((dma_addr >> 32) & 0x1F)|tmp;
+ CE_SRC_RING_BASE_ADDR_HIGH_SET(scn,
+ ctrl_addr, (uint32_t)dma_addr);
+ }
+#endif
+ CE_SRC_RING_SZ_SET(scn, ctrl_addr, nentries);
+ CE_SRC_RING_DMAX_SET(scn, 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(scn, ctrl_addr, 1);
+#endif
+ CE_SRC_RING_LOWMARK_SET(scn, ctrl_addr, 0);
+ CE_SRC_RING_HIGHMARK_SET(scn, ctrl_addr, nentries);
+ A_TARGET_ACCESS_END_RET_PTR(scn);
+ }
+ }
+
+ /* destination ring setup */
+ nentries = attr->dest_nentries;
+ if (nentries) {
+ struct CE_ring_state *dest_ring;
+ unsigned CE_nbytes;
+ char *ptr;
+ uint64_t dma_addr;
+
+ nentries = roundup_pwr2(nentries);
+ if (CE_state->dest_ring) {
+ CDF_ASSERT(CE_state->dest_ring->nentries == nentries);
+ } else {
+ CE_nbytes = sizeof(struct CE_ring_state)
+ + (nentries * sizeof(void *));
+ ptr = cdf_mem_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.
+ */
+ HIF_ERROR("%s: dest ring has no mem",
+ __func__);
+ if (malloc_src_ring) {
+ cdf_mem_free(CE_state->src_ring);
+ CE_state->src_ring = NULL;
+ malloc_src_ring = false;
+ }
+ if (malloc_CE_state) {
+ /* allocated CE_state locally */
+ cdf_spin_lock(&scn->target_lock);
+ scn->ce_id_to_state[CE_id] = NULL;
+ cdf_spin_unlock(&scn->target_lock);
+ cdf_mem_free(CE_state);
+ malloc_CE_state = false;
+ }
+ return NULL;
+ }
+ cdf_mem_zero(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(scn);
+ dest_ring->sw_index =
+ CE_DEST_RING_READ_IDX_GET(scn, ctrl_addr);
+ dest_ring->write_index =
+ CE_DEST_RING_WRITE_IDX_GET(scn, ctrl_addr);
+ A_TARGET_ACCESS_END_RET_PTR(scn);
+ 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 =
+ cdf_os_mem_alloc_consistent(scn->cdf_dev,
+ (nentries *
+ sizeof(struct CE_dest_desc) +
+ CE_DESC_RING_ALIGN),
+ &base_addr, 0);
+ if (dest_ring->base_addr_owner_space_unaligned
+ == NULL) {
+ HIF_ERROR("%s: dest ring has no DMA mem",
+ __func__);
+ 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
+ */
+ cdf_mem_zero(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(scn);
+ dma_addr = dest_ring->base_addr_CE_space;
+ CE_DEST_RING_BASE_ADDR_SET(scn, ctrl_addr,
+ (uint32_t)(dma_addr & 0xFFFFFFFF));
+#ifdef WLAN_ENABLE_QCA6180
+ {
+ uint32_t tmp;
+ tmp = CE_DEST_RING_BASE_ADDR_HIGH_GET(scn,
+ ctrl_addr);
+ tmp &= ~0x1F;
+ dma_addr = ((dma_addr >> 32) & 0x1F)|tmp;
+ CE_DEST_RING_BASE_ADDR_HIGH_SET(scn,
+ ctrl_addr, (uint32_t)dma_addr);
+ }
+#endif
+ CE_DEST_RING_SZ_SET(scn, ctrl_addr, nentries);
+#ifdef BIG_ENDIAN_HOST
+ /* Enable Dest ring byte swap for big endian host */
+ CE_DEST_RING_BYTE_SWAP_SET(scn, ctrl_addr, 1);
+#endif
+ CE_DEST_RING_LOWMARK_SET(scn, ctrl_addr, 0);
+ CE_DEST_RING_HIGHMARK_SET(scn, ctrl_addr, nentries);
+ A_TARGET_ACCESS_END_RET_PTR(scn);
+
+ /* epping */
+ /* poll timer */
+ if ((CE_state->attr_flags & CE_ATTR_ENABLE_POLL)) {
+ cdf_softirq_timer_init(scn->cdf_dev,
+ &CE_state->poll_timer,
+ ce_poll_timeout,
+ CE_state,
+ CDF_TIMER_TYPE_SW);
+ CE_state->timer_inited = true;
+ cdf_softirq_timer_mod(&CE_state->poll_timer,
+ CE_POLL_TIMEOUT);
+ }
+ }
+ }
+
+ /* Enable CE error interrupts */
+ A_TARGET_ACCESS_BEGIN_RET_PTR(scn);
+ CE_ERROR_INTR_ENABLE(scn, ctrl_addr);
+ A_TARGET_ACCESS_END_RET_PTR(scn);
+
+ return (struct CE_handle *)CE_state;
+
+error_no_dma_mem:
+ ce_fini((struct CE_handle *)CE_state);
+ return NULL;
+}
+
+#ifdef WLAN_FEATURE_FASTPATH
+/**
+ * ce_h2t_tx_ce_cleanup() Place holder function for H2T CE cleanup.
+ * No processing is required inside this function.
+ * @ce_hdl: Cope engine handle
+ * Using an assert, this function makes sure that,
+ * the TX CE has been processed completely.
+ * Return: none
+ */
+void
+ce_h2t_tx_ce_cleanup(struct CE_handle *ce_hdl)
+{
+ struct CE_state *ce_state = (struct CE_state *)ce_hdl;
+ struct CE_ring_state *src_ring = ce_state->src_ring;
+ struct ol_softc *sc = ce_state->scn;
+ uint32_t sw_index, write_index;
+
+ if (sc->fastpath_mode_on && (ce_state->id == CE_HTT_H2T_MSG)) {
+ HIF_INFO("%s %d Fastpath mode ON, Cleaning up HTT Tx CE\n",
+ __func__, __LINE__);
+ cdf_spin_lock_bh(&sc->target_lock);
+ sw_index = src_ring->sw_index;
+ write_index = src_ring->sw_index;
+ cdf_spin_unlock_bh(&sc->target_lock);
+
+ /* At this point Tx CE should be clean */
+ cdf_assert_always(sw_index == write_index);
+ }
+}
+#else
+void ce_h2t_tx_ce_cleanup(struct CE_handle *ce_hdl)
+{
+}
+#endif /* WLAN_FEATURE_FASTPATH */
+
+void ce_fini(struct CE_handle *copyeng)
+{
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ unsigned int CE_id = CE_state->id;
+ struct ol_softc *scn = CE_state->scn;
+
+ CE_state->state = CE_UNUSED;
+ scn->ce_id_to_state[CE_id] = NULL;
+ if (CE_state->src_ring) {
+ /* Cleanup the HTT Tx ring */
+ ce_h2t_tx_ce_cleanup(copyeng);
+
+ if (CE_state->src_ring->shadow_base_unaligned)
+ cdf_mem_free(CE_state->src_ring->shadow_base_unaligned);
+ if (CE_state->src_ring->base_addr_owner_space_unaligned)
+ cdf_os_mem_free_consistent(scn->cdf_dev,
+ (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, 0);
+ cdf_mem_free(CE_state->src_ring);
+ }
+ if (CE_state->dest_ring) {
+ if (CE_state->dest_ring->base_addr_owner_space_unaligned)
+ cdf_os_mem_free_consistent(scn->cdf_dev,
+ (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, 0);
+ cdf_mem_free(CE_state->dest_ring);
+
+ /* epping */
+ if (CE_state->timer_inited) {
+ CE_state->timer_inited = false;
+ cdf_softirq_timer_free(&CE_state->poll_timer);
+ }
+ }
+ cdf_mem_free(CE_state);
+}
+
+void hif_detach_htc(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ cdf_mem_zero(&hif_state->msg_callbacks_pending,
+ sizeof(hif_state->msg_callbacks_pending));
+ cdf_mem_zero(&hif_state->msg_callbacks_current,
+ sizeof(hif_state->msg_callbacks_current));
+}
+
+/* Send the first nbytes bytes of the buffer */
+CDF_STATUS
+hif_send_head(struct ol_softc *scn,
+ uint8_t pipe, unsigned int transfer_id, unsigned int nbytes,
+ cdf_nbuf_t nbuf, unsigned int data_attr)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ struct HIF_CE_pipe_info *pipe_info = &(hif_state->pipe_info[pipe]);
+ struct CE_handle *ce_hdl = pipe_info->ce_hdl;
+ int bytes = nbytes, nfrags = 0;
+ struct ce_sendlist sendlist;
+ int status, i = 0;
+ unsigned int mux_id = 0;
+
+ CDF_ASSERT(nbytes <= cdf_nbuf_len(nbuf));
+
+ transfer_id =
+ (mux_id & MUX_ID_MASK) |
+ (transfer_id & TRANSACTION_ID_MASK);
+ data_attr &= DESC_DATA_FLAG_MASK;
+ /*
+ * The common case involves sending multiple fragments within a
+ * single download (the tx descriptor and the tx frame header).
+ * So, optimize for the case of multiple fragments by not even
+ * checking whether it's necessary to use a sendlist.
+ * The overhead of using a sendlist for a single buffer download
+ * is not a big deal, since it happens rarely (for WMI messages).
+ */
+ ce_sendlist_init(&sendlist);
+ do {
+ uint32_t frag_paddr;
+ int frag_bytes;
+
+ frag_paddr = cdf_nbuf_get_frag_paddr_lo(nbuf, nfrags);
+ frag_bytes = cdf_nbuf_get_frag_len(nbuf, nfrags);
+ /*
+ * Clear the packet offset for all but the first CE desc.
+ */
+ if (i++ > 0)
+ data_attr &= ~CDF_CE_TX_PKT_OFFSET_BIT_M;
+
+ status = ce_sendlist_buf_add(&sendlist, frag_paddr,
+ frag_bytes >
+ bytes ? bytes : frag_bytes,
+ cdf_nbuf_get_frag_is_wordstream
+ (nbuf,
+ nfrags) ? 0 :
+ CE_SEND_FLAG_SWAP_DISABLE,
+ data_attr);
+ if (status != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: error, frag_num %d larger than limit",
+ __func__, nfrags);
+ return status;
+ }
+ bytes -= frag_bytes;
+ nfrags++;
+ } while (bytes > 0);
+
+ /* Make sure we have resources to handle this request */
+ cdf_spin_lock_bh(&pipe_info->completion_freeq_lock);
+ if (pipe_info->num_sends_allowed < nfrags) {
+ cdf_spin_unlock_bh(&pipe_info->completion_freeq_lock);
+ ce_pkt_error_count_incr(hif_state, HIF_PIPE_NO_RESOURCE);
+ return CDF_STATUS_E_RESOURCES;
+ }
+ pipe_info->num_sends_allowed -= nfrags;
+ cdf_spin_unlock_bh(&pipe_info->completion_freeq_lock);
+
+ if (cdf_unlikely(ce_hdl == NULL)) {
+ HIF_ERROR("%s: error CE handle is null", __func__);
+ return A_ERROR;
+ }
+
+ NBUF_UPDATE_TX_PKT_COUNT(nbuf, NBUF_TX_PKT_HIF);
+ DPTRACE(cdf_dp_trace(nbuf, CDF_DP_TRACE_HIF_PACKET_PTR_RECORD,
+ (uint8_t *)(cdf_nbuf_data(nbuf)),
+ sizeof(cdf_nbuf_data(nbuf))));
+ status = ce_sendlist_send(ce_hdl, nbuf, &sendlist, transfer_id);
+ CDF_ASSERT(status == CDF_STATUS_SUCCESS);
+
+ return status;
+}
+
+void hif_send_complete_check(struct ol_softc *scn, uint8_t pipe, int force)
+{
+ if (!force) {
+ int resources;
+ /*
+ * Decide whether to actually poll for completions, or just
+ * wait for a later chance. If there seem to be plenty of
+ * resources left, then just wait, since checking involves
+ * reading a CE register, which is a relatively expensive
+ * operation.
+ */
+ resources = hif_get_free_queue_number(scn, pipe);
+ /*
+ * If at least 50% of the total resources are still available,
+ * don't bother checking again yet.
+ */
+ if (resources > (host_ce_config[pipe].src_nentries >> 1)) {
+ return;
+ }
+ }
+#ifdef ATH_11AC_TXCOMPACT
+ ce_per_engine_servicereap(scn, pipe);
+#else
+ ce_per_engine_service(scn, pipe);
+#endif
+}
+
+uint16_t hif_get_free_queue_number(struct ol_softc *scn, uint8_t pipe)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ struct HIF_CE_pipe_info *pipe_info = &(hif_state->pipe_info[pipe]);
+ uint16_t rv;
+
+ cdf_spin_lock_bh(&pipe_info->completion_freeq_lock);
+ rv = pipe_info->num_sends_allowed;
+ cdf_spin_unlock_bh(&pipe_info->completion_freeq_lock);
+ return rv;
+}
+
+/* Called by lower (CE) layer when a send to Target completes. */
+void
+hif_pci_ce_send_done(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t CE_data,
+ unsigned int nbytes, unsigned int transfer_id,
+ unsigned int sw_index, unsigned int hw_index,
+ unsigned int toeplitz_hash_result)
+{
+ struct HIF_CE_pipe_info *pipe_info =
+ (struct HIF_CE_pipe_info *)ce_context;
+ struct HIF_CE_state *hif_state = pipe_info->HIF_CE_state;
+ struct HIF_CE_completion_state *compl_state;
+ struct HIF_CE_completion_state *compl_queue_head, *compl_queue_tail;
+ unsigned int sw_idx = sw_index, hw_idx = hw_index;
+
+ compl_queue_head = compl_queue_tail = NULL;
+ do {
+ /*
+ * For the send completion of an item in sendlist, just increment
+ * num_sends_allowed. The upper layer callback will be triggered
+ * when last fragment is done with send.
+ */
+ if (transfer_context == CE_SENDLIST_ITEM_CTXT) {
+ cdf_spin_lock(&pipe_info->completion_freeq_lock);
+ pipe_info->num_sends_allowed++;
+ cdf_spin_unlock(&pipe_info->completion_freeq_lock);
+ continue;
+ }
+
+ cdf_spin_lock(&pipe_info->completion_freeq_lock);
+ compl_state = pipe_info->completion_freeq_head;
+ if (!compl_state) {
+ cdf_spin_unlock(&pipe_info->completion_freeq_lock);
+ HIF_ERROR("%s: ce_id:%d num_allowed:%d pipe_info:%p",
+ __func__, pipe_info->pipe_num,
+ pipe_info->num_sends_allowed,
+ pipe_info);
+ ASSERT(0);
+ break;
+ }
+ pipe_info->completion_freeq_head = compl_state->next;
+ cdf_spin_unlock(&pipe_info->completion_freeq_lock);
+
+ compl_state->next = NULL;
+ compl_state->send_or_recv = HIF_CE_COMPLETE_SEND;
+ compl_state->copyeng = copyeng;
+ compl_state->ce_context = ce_context;
+ compl_state->transfer_context = transfer_context;
+ compl_state->data = CE_data;
+ compl_state->nbytes = nbytes;
+ compl_state->transfer_id = transfer_id;
+ compl_state->flags = 0;
+ compl_state->toeplitz_hash_result = toeplitz_hash_result;
+
+ /* Enqueue at end of local queue */
+ if (compl_queue_tail) {
+ compl_queue_tail->next = compl_state;
+ } else {
+ compl_queue_head = compl_state;
+ }
+ compl_queue_tail = compl_state;
+ } while (ce_completed_send_next(copyeng,
+ &ce_context, &transfer_context,
+ &CE_data, &nbytes, &transfer_id,
+ &sw_idx, &hw_idx,
+ &toeplitz_hash_result) == CDF_STATUS_SUCCESS);
+
+ if (compl_queue_head == NULL) {
+ /*
+ * If only some of the items within a sendlist have completed,
+ * don't invoke completion processing until the entire sendlist
+ * has been sent.
+ */
+ return;
+ }
+
+ cdf_spin_lock(&hif_state->completion_pendingq_lock);
+
+ /* Enqueue the local completion queue on the
+ * per-device completion queue */
+ if (hif_state->completion_pendingq_head) {
+ hif_state->completion_pendingq_tail->next = compl_queue_head;
+ hif_state->completion_pendingq_tail = compl_queue_tail;
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+ } else {
+ hif_state->completion_pendingq_head = compl_queue_head;
+ hif_state->completion_pendingq_tail = compl_queue_tail;
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+
+ /* Alert the send completion service thread */
+ hif_completion_thread(hif_state);
+ }
+}
+
+/* Called by lower (CE) layer when data is received from the Target. */
+void
+hif_pci_ce_recv_data(struct CE_handle *copyeng, void *ce_context,
+ void *transfer_context, cdf_dma_addr_t CE_data,
+ unsigned int nbytes, unsigned int transfer_id,
+ unsigned int flags)
+{
+ struct HIF_CE_pipe_info *pipe_info =
+ (struct HIF_CE_pipe_info *)ce_context;
+ struct HIF_CE_state *hif_state = pipe_info->HIF_CE_state;
+ struct ol_softc *scn = hif_state->scn;
+ struct HIF_CE_completion_state *compl_state;
+ struct HIF_CE_completion_state *compl_queue_head, *compl_queue_tail;
+
+ compl_queue_head = compl_queue_tail = NULL;
+ do {
+ cdf_spin_lock(&pipe_info->completion_freeq_lock);
+ compl_state = pipe_info->completion_freeq_head;
+ ASSERT(compl_state != NULL);
+ pipe_info->completion_freeq_head = compl_state->next;
+ cdf_spin_unlock(&pipe_info->completion_freeq_lock);
+
+ compl_state->next = NULL;
+ compl_state->send_or_recv = HIF_CE_COMPLETE_RECV;
+ compl_state->copyeng = copyeng;
+ compl_state->ce_context = ce_context;
+ compl_state->transfer_context = transfer_context;
+ compl_state->data = CE_data;
+ compl_state->nbytes = nbytes;
+ compl_state->transfer_id = transfer_id;
+ compl_state->flags = flags;
+
+ /* Enqueue at end of local queue */
+ if (compl_queue_tail) {
+ compl_queue_tail->next = compl_state;
+ } else {
+ compl_queue_head = compl_state;
+ }
+ compl_queue_tail = compl_state;
+
+ cdf_nbuf_unmap_single(scn->cdf_dev,
+ (cdf_nbuf_t) transfer_context,
+ CDF_DMA_FROM_DEVICE);
+
+ /*
+ * 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
+ */
+
+ /* Set up force_break flag if num of receices reaches
+ * MAX_NUM_OF_RECEIVES */
+ scn->receive_count++;
+ if (cdf_unlikely(hif_max_num_receives_reached(
+ scn->receive_count))) {
+ scn->force_break = 1;
+ break;
+ }
+ } while (ce_completed_recv_next(copyeng, &ce_context, &transfer_context,
+ &CE_data, &nbytes, &transfer_id,
+ &flags) == CDF_STATUS_SUCCESS);
+
+ cdf_spin_lock(&hif_state->completion_pendingq_lock);
+
+ /* Enqueue the local completion queue on the
+ * per-device completion queue */
+ if (hif_state->completion_pendingq_head) {
+ hif_state->completion_pendingq_tail->next = compl_queue_head;
+ hif_state->completion_pendingq_tail = compl_queue_tail;
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+ } else {
+ hif_state->completion_pendingq_head = compl_queue_head;
+ hif_state->completion_pendingq_tail = compl_queue_tail;
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+
+ /* Alert the recv completion service thread */
+ hif_completion_thread(hif_state);
+ }
+}
+
+/* TBDXXX: Set CE High Watermark; invoke txResourceAvailHandler in response */
+
+void
+hif_post_init(struct ol_softc *scn, void *unused,
+ struct hif_msg_callbacks *callbacks)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+#ifdef CONFIG_ATH_PCIE_ACCESS_DEBUG
+ spin_lock_init(&pcie_access_log_lock);
+#endif
+ /* Save callbacks for later installation */
+ cdf_mem_copy(&hif_state->msg_callbacks_pending, callbacks,
+ sizeof(hif_state->msg_callbacks_pending));
+
+}
+
+static void hif_pci_free_complete_state(struct HIF_CE_pipe_info *pipe_info)
+{
+ struct HIF_CE_completion_state_list *tmp_list;
+
+ while (pipe_info->completion_space_list) {
+ tmp_list = pipe_info->completion_space_list;
+ pipe_info->completion_space_list = tmp_list->next;
+ cdf_mem_free(tmp_list);
+ }
+}
+
+static int hif_alloc_complete_state_list(
+ struct HIF_CE_pipe_info *pipe_info,
+ int completions_needed)
+{
+ struct HIF_CE_completion_state *compl_state;
+ struct HIF_CE_completion_state_list *tmp_list;
+ int i;
+ int idx;
+ int num_list;
+ int allocated_node;
+ int num_in_batch;
+ size_t len;
+
+ allocated_node = 0;
+ num_list = (completions_needed + HIF_CE_COMPLETE_STATE_NUM -1);
+ num_list /= HIF_CE_COMPLETE_STATE_NUM;
+ for (idx = 0; idx < num_list; idx++) {
+ if (completions_needed - allocated_node >=
+ HIF_CE_COMPLETE_STATE_NUM)
+ num_in_batch = HIF_CE_COMPLETE_STATE_NUM;
+ else
+ num_in_batch = completions_needed - allocated_node;
+ if (num_in_batch <= 0)
+ break;
+ len = num_in_batch * sizeof(struct HIF_CE_completion_state) +
+ sizeof(struct HIF_CE_completion_state_list);
+ /* Allocate structures to track pending send/recv completions */
+ tmp_list =
+ (struct HIF_CE_completion_state_list *)
+ cdf_mem_malloc(len);
+ if (!tmp_list) {
+ HIF_ERROR("%s: compl_state has no mem", __func__);
+ hif_pci_free_complete_state(pipe_info);
+ return -1;
+ }
+ cdf_mem_zero(tmp_list, len);
+ compl_state = (struct HIF_CE_completion_state *)
+ ((uint8_t *)tmp_list +
+ sizeof(struct HIF_CE_completion_state_list));
+ for (i = 0; i < num_in_batch; i++) {
+ compl_state->send_or_recv = HIF_CE_COMPLETE_FREE;
+ compl_state->next = NULL;
+ if (pipe_info->completion_freeq_head)
+ pipe_info->completion_freeq_tail->next =
+ compl_state;
+ else
+ pipe_info->completion_freeq_head =
+ compl_state;
+ pipe_info->completion_freeq_tail = compl_state;
+ compl_state++;
+ allocated_node++;
+ }
+ if (pipe_info->completion_space_list == NULL) {
+ pipe_info->completion_space_list = tmp_list;
+ tmp_list->next = NULL;
+ } else {
+ tmp_list->next =
+ pipe_info->completion_space_list;
+ pipe_info->completion_space_list = tmp_list;
+ }
+ }
+ cdf_spinlock_init(&pipe_info->completion_freeq_lock);
+ return 0;
+}
+int hif_completion_thread_startup(struct HIF_CE_state *hif_state)
+{
+ struct CE_handle *ce_diag = hif_state->ce_diag;
+ int pipe_num;
+ struct ol_softc *scn = hif_state->scn;
+
+ /* daemonize("hif_compl_thread"); */
+
+ cdf_spinlock_init(&hif_state->completion_pendingq_lock);
+ hif_state->completion_pendingq_head =
+ hif_state->completion_pendingq_tail = NULL;
+
+ if (scn->ce_count == 0) {
+ HIF_ERROR("%s: Invalid ce_count\n", __func__);
+ return -EINVAL;
+ }
+ A_TARGET_ACCESS_LIKELY(scn);
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ struct CE_attr attr;
+ struct HIF_CE_pipe_info *pipe_info;
+ int completions_needed;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ if (pipe_info->ce_hdl == ce_diag) {
+ continue; /* Handle Diagnostic CE specially */
+ }
+ attr = host_ce_config[pipe_num];
+ completions_needed = 0;
+ if (attr.src_nentries) {
+ /* pipe used to send to target */
+ HIF_INFO_MED("%s: pipe_num:%d pipe_info:0x%p",
+ __func__, pipe_num, pipe_info);
+ ce_send_cb_register(pipe_info->ce_hdl,
+ hif_pci_ce_send_done, pipe_info,
+ attr.flags & CE_ATTR_DISABLE_INTR);
+ completions_needed += attr.src_nentries;
+ pipe_info->num_sends_allowed = attr.src_nentries - 1;
+ }
+ if (attr.dest_nentries) {
+ /* pipe used to receive from target */
+ ce_recv_cb_register(pipe_info->ce_hdl,
+ hif_pci_ce_recv_data, pipe_info,
+ attr.flags & CE_ATTR_DISABLE_INTR);
+ completions_needed += attr.dest_nentries;
+ }
+
+ pipe_info->completion_freeq_head =
+ pipe_info->completion_freeq_tail = NULL;
+ if (completions_needed > 0) {
+ int ret;
+
+ ret = hif_alloc_complete_state_list(pipe_info,
+ completions_needed);
+ if (ret != 0) {
+ HIF_ERROR("%s: ce_id = %d, no mem",
+ __func__, pipe_info->pipe_num);
+ return ret;
+ }
+ }
+ }
+ A_TARGET_ACCESS_UNLIKELY(scn);
+ return 0;
+}
+
+void hif_completion_thread_shutdown(struct HIF_CE_state *hif_state)
+{
+ struct HIF_CE_completion_state *compl_state;
+ struct HIF_CE_pipe_info *pipe_info;
+ struct ol_softc *scn = hif_state->scn;
+ int pipe_num;
+
+ /*
+ * Drop pending completions. These have already been
+ * reported by the CE layer to us but we have not yet
+ * passed them upstack.
+ */
+ while ((compl_state = hif_state->completion_pendingq_head) != NULL) {
+ cdf_nbuf_t netbuf;
+
+ netbuf = (cdf_nbuf_t) compl_state->transfer_context;
+ cdf_nbuf_free(netbuf);
+
+ hif_state->completion_pendingq_head = compl_state->next;
+
+ /*
+ * NB: Don't bother to place compl_state on pipe's free queue,
+ * because we'll free underlying memory for the free queues
+ * in a moment anyway.
+ */
+ }
+
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ hif_pci_free_complete_state(pipe_info);
+ cdf_spinlock_destroy(&pipe_info->completion_freeq_lock);
+ }
+
+ /* hif_state->compl_thread = NULL; */
+ /* complete_and_exit(&hif_state->compl_thread_done, 0); */
+}
+
+/*
+ * This thread provides a context in which send/recv completions
+ * are handled.
+ *
+ * Note: HIF installs callback functions with the CE layer.
+ * Those functions are called directly (e.g. in interrupt context).
+ * Upper layers (e.g. HTC) have installed callbacks with HIF which
+ * expect to be called in a thread context. This is where that
+ * conversion occurs.
+ *
+ * TBDXXX: Currently we use just one thread for all pipes.
+ * This might be sufficient or we might need multiple threads.
+ */
+int
+/* hif_completion_thread(void *hif_dev) */
+hif_completion_thread(struct HIF_CE_state *hif_state)
+{
+ struct hif_msg_callbacks *msg_callbacks =
+ &hif_state->msg_callbacks_current;
+ struct HIF_CE_completion_state *compl_state;
+
+ /* Allow only one instance of the thread to execute at a time to
+ * prevent out of order processing of messages - this is bad for higher
+ * layer code
+ */
+ if (!cdf_atomic_dec_and_test(&hif_state->hif_thread_idle)) {
+ /* We were not the lucky one */
+ cdf_atomic_inc(&hif_state->hif_thread_idle);
+ return 0;
+ }
+
+ if (!msg_callbacks->fwEventHandler
+ || !msg_callbacks->txCompletionHandler
+ || !msg_callbacks->rxCompletionHandler) {
+ return 0;
+ }
+ while (atomic_read(&hif_state->fw_event_pending) > 0) {
+ /*
+ * Clear pending state before handling, in case there's
+ * another while we process the first.
+ */
+ atomic_set(&hif_state->fw_event_pending, 0);
+ msg_callbacks->fwEventHandler(msg_callbacks->Context,
+ CDF_STATUS_E_FAILURE);
+ }
+
+ if (hif_state->scn->target_status == OL_TRGET_STATUS_RESET)
+ return 0;
+
+ for (;; ) {
+ struct HIF_CE_pipe_info *pipe_info;
+ int send_done = 0;
+
+ cdf_spin_lock(&hif_state->completion_pendingq_lock);
+
+ if (!hif_state->completion_pendingq_head) {
+ /* We are atomically sure that
+ * there is no pending work */
+ cdf_atomic_inc(&hif_state->hif_thread_idle);
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+ break; /* All pending completions are handled */
+ }
+
+ /* Dequeue the first unprocessed but completed transfer */
+ compl_state = hif_state->completion_pendingq_head;
+ hif_state->completion_pendingq_head = compl_state->next;
+ cdf_spin_unlock(&hif_state->completion_pendingq_lock);
+
+ pipe_info = (struct HIF_CE_pipe_info *)compl_state->ce_context;
+ if (compl_state->send_or_recv == HIF_CE_COMPLETE_SEND) {
+ msg_callbacks->txCompletionHandler(msg_callbacks->
+ Context,
+ compl_state->
+ transfer_context,
+ compl_state->
+ transfer_id,
+ compl_state->toeplitz_hash_result);
+ send_done = 1;
+ } else {
+ /* compl_state->send_or_recv == HIF_CE_COMPLETE_RECV */
+ cdf_nbuf_t netbuf;
+ unsigned int nbytes;
+
+ atomic_inc(&pipe_info->recv_bufs_needed);
+ hif_post_recv_buffers(hif_state->scn);
+
+ netbuf = (cdf_nbuf_t) compl_state->transfer_context;
+ nbytes = compl_state->nbytes;
+ /*
+ * To see the following debug output,
+ * enable the HIF_PCI_DEBUG flag in
+ * the debug module declaration in this source file
+ */
+ HIF_DBG("%s: netbuf=%p, nbytes=%d",
+ __func__, netbuf, nbytes);
+ if (nbytes <= pipe_info->buf_sz) {
+ cdf_nbuf_set_pktlen(netbuf, nbytes);
+ msg_callbacks->
+ rxCompletionHandler(msg_callbacks->Context,
+ netbuf,
+ pipe_info->pipe_num);
+ } else {
+ HIF_ERROR(
+ "%s: Invalid Rx msg buf:%p nbytes:%d",
+ __func__, netbuf, nbytes);
+ cdf_nbuf_free(netbuf);
+ }
+ }
+
+ /* Recycle completion state back to the pipe it came from. */
+ compl_state->next = NULL;
+ compl_state->send_or_recv = HIF_CE_COMPLETE_FREE;
+ cdf_spin_lock(&pipe_info->completion_freeq_lock);
+ if (pipe_info->completion_freeq_head) {
+ pipe_info->completion_freeq_tail->next = compl_state;
+ } else {
+ pipe_info->completion_freeq_head = compl_state;
+ }
+ pipe_info->completion_freeq_tail = compl_state;
+ pipe_info->num_sends_allowed += send_done;
+ cdf_spin_unlock(&pipe_info->completion_freeq_lock);
+ }
+
+ return 0;
+}
+
+/*
+ * Install pending msg callbacks.
+ *
+ * TBDXXX: This hack is needed because upper layers install msg callbacks
+ * for use with HTC before BMI is done; yet this HIF implementation
+ * needs to continue to use BMI msg callbacks. Really, upper layers
+ * should not register HTC callbacks until AFTER BMI phase.
+ */
+static void hif_msg_callbacks_install(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ cdf_mem_copy(&hif_state->msg_callbacks_current,
+ &hif_state->msg_callbacks_pending,
+ sizeof(hif_state->msg_callbacks_pending));
+}
+
+void hif_claim_device(struct ol_softc *scn, void *claimedContext)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ hif_state->claimedContext = claimedContext;
+}
+
+void hif_release_device(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ hif_state->claimedContext = NULL;
+}
+
+void
+hif_get_default_pipe(struct ol_softc *scn, uint8_t *ULPipe, uint8_t *DLPipe)
+{
+ int ul_is_polled, dl_is_polled;
+
+ (void)hif_map_service_to_pipe(scn, HTC_CTRL_RSVD_SVC,
+ ULPipe, DLPipe, &ul_is_polled, &dl_is_polled);
+}
+
+/* TBDXXX - temporary mapping while we have too few CE's */
+int
+hif_map_service_to_pipe(struct ol_softc *scn, uint16_t ServiceId,
+ uint8_t *ULPipe, uint8_t *DLPipe, int *ul_is_polled,
+ int *dl_is_polled)
+{
+ int status = CDF_STATUS_SUCCESS;
+
+ *dl_is_polled = 0; /* polling for received messages not supported */
+ switch (ServiceId) {
+ case HTT_DATA_MSG_SVC:
+ /*
+ * Host->target HTT gets its own pipe, so it can be polled
+ * while other pipes are interrupt driven.
+ */
+ *ULPipe = 4;
+ /*
+ * Use the same target->host pipe for HTC ctrl,
+ * HTC raw streams, and HTT.
+ */
+ *DLPipe = 1;
+ break;
+
+ case HTC_CTRL_RSVD_SVC:
+ *ULPipe = 0;
+ *DLPipe = 2;
+ break;
+
+ case HTC_RAW_STREAMS_SVC:
+ /*
+ * Note: HTC_RAW_STREAMS_SVC is currently unused, and
+ * HTC_CTRL_RSVD_SVC could share the same pipe as the
+ * WMI services. So, if another CE is needed, change
+ * this to *ULPipe = 3, which frees up CE 0.
+ */
+ /**ULPipe = 3; */
+ *ULPipe = 0;
+ *DLPipe = 2;
+ break;
+
+ case WMI_DATA_BK_SVC:
+ /*
+ * To avoid some confusions, better to introduce new EP-ping
+ * service instead of using existed services. Until the main
+ * framework support this, keep this design.
+ */
+ if (WLAN_IS_EPPING_ENABLED(cds_get_conparam())) {
+ *ULPipe = 4;
+ *DLPipe = 1;
+ break;
+ }
+ case WMI_DATA_BE_SVC:
+ case WMI_DATA_VI_SVC:
+ case WMI_DATA_VO_SVC:
+
+ case WMI_CONTROL_SVC:
+ *ULPipe = 3;
+ *DLPipe = 2;
+ break;
+
+ case WDI_IPA_TX_SVC:
+ *ULPipe = 5;
+ break;
+
+ /* pipe 5 unused */
+ /* pipe 6 reserved */
+ /* pipe 7 reserved */
+
+ default:
+ status = CDF_STATUS_E_INVAL;
+ break;
+ }
+ *ul_is_polled =
+ (host_ce_config[*ULPipe].flags & CE_ATTR_DISABLE_INTR) != 0;
+
+ return status;
+}
+
+/**
+ * hif_dump_pipe_debug_count() - Log error count
+ * @scn: ol_softc pointer.
+ *
+ * Output the pipe error counts of each pipe to log file
+ *
+ * Return: N/A
+ */
+void hif_dump_pipe_debug_count(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state;
+ int pipe_num;
+
+ if (scn == NULL) {
+ HIF_ERROR("%s scn is NULL", __func__);
+ return;
+ }
+ hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ if (hif_state == NULL) {
+ HIF_ERROR("%s hif_state is NULL", __func__);
+ return;
+ }
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ struct HIF_CE_pipe_info *pipe_info;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+
+ if (pipe_info->nbuf_alloc_err_count > 0 ||
+ pipe_info->nbuf_dma_err_count > 0 ||
+ pipe_info->nbuf_ce_enqueue_err_count)
+ HIF_ERROR(
+ "%s: pipe_id = %d, recv_bufs_needed = %d, nbuf_alloc_err_count = %u, nbuf_dma_err_count = %u, nbuf_ce_enqueue_err_count = %u",
+ __func__, pipe_info->pipe_num,
+ atomic_read(&pipe_info->recv_bufs_needed),
+ pipe_info->nbuf_alloc_err_count,
+ pipe_info->nbuf_dma_err_count,
+ pipe_info->nbuf_ce_enqueue_err_count);
+ }
+}
+
+static int hif_post_recv_buffers_for_pipe(struct HIF_CE_pipe_info *pipe_info)
+{
+ struct CE_handle *ce_hdl;
+ cdf_size_t buf_sz;
+ struct HIF_CE_state *hif_state = pipe_info->HIF_CE_state;
+ struct ol_softc *scn = hif_state->scn;
+ CDF_STATUS ret;
+ uint32_t bufs_posted = 0;
+
+ buf_sz = pipe_info->buf_sz;
+ if (buf_sz == 0) {
+ /* Unused Copy Engine */
+ return 0;
+ }
+
+ ce_hdl = pipe_info->ce_hdl;
+
+ cdf_spin_lock_bh(&pipe_info->recv_bufs_needed_lock);
+ while (atomic_read(&pipe_info->recv_bufs_needed) > 0) {
+ cdf_dma_addr_t CE_data; /* CE space buffer address */
+ cdf_nbuf_t nbuf;
+ int status;
+
+ atomic_dec(&pipe_info->recv_bufs_needed);
+ cdf_spin_unlock_bh(&pipe_info->recv_bufs_needed_lock);
+
+ nbuf = cdf_nbuf_alloc(scn->cdf_dev, buf_sz, 0, 4, false);
+ if (!nbuf) {
+ cdf_spin_lock_bh(&pipe_info->recv_bufs_needed_lock);
+ pipe_info->nbuf_alloc_err_count++;
+ cdf_spin_unlock_bh(
+ &pipe_info->recv_bufs_needed_lock);
+ HIF_ERROR(
+ "%s buf alloc error [%d] needed %d, nbuf_alloc_err_count = %u",
+ __func__, pipe_info->pipe_num,
+ atomic_read(&pipe_info->recv_bufs_needed),
+ pipe_info->nbuf_alloc_err_count);
+ atomic_inc(&pipe_info->recv_bufs_needed);
+ return 1;
+ }
+
+ /*
+ * cdf_nbuf_peek_header(nbuf, &data, &unused);
+ * CE_data = dma_map_single(dev, data, buf_sz, );
+ * DMA_FROM_DEVICE);
+ */
+ ret =
+ cdf_nbuf_map_single(scn->cdf_dev, nbuf,
+ CDF_DMA_FROM_DEVICE);
+
+ if (unlikely(ret != CDF_STATUS_SUCCESS)) {
+ cdf_spin_lock_bh(&pipe_info->recv_bufs_needed_lock);
+ pipe_info->nbuf_dma_err_count++;
+ cdf_spin_unlock_bh(&pipe_info->recv_bufs_needed_lock);
+ HIF_ERROR(
+ "%s buf alloc error [%d] needed %d, nbuf_dma_err_count = %u",
+ __func__, pipe_info->pipe_num,
+ atomic_read(&pipe_info->recv_bufs_needed),
+ pipe_info->nbuf_dma_err_count);
+ cdf_nbuf_free(nbuf);
+ atomic_inc(&pipe_info->recv_bufs_needed);
+ return 1;
+ }
+
+ CE_data = cdf_nbuf_get_frag_paddr_lo(nbuf, 0);
+
+ cdf_os_mem_dma_sync_single_for_device(scn->cdf_dev, CE_data,
+ buf_sz, DMA_FROM_DEVICE);
+ status = ce_recv_buf_enqueue(ce_hdl, (void *)nbuf, CE_data);
+ CDF_ASSERT(status == CDF_STATUS_SUCCESS);
+ if (status != EOK) {
+ cdf_spin_lock_bh(&pipe_info->recv_bufs_needed_lock);
+ pipe_info->nbuf_ce_enqueue_err_count++;
+ cdf_spin_unlock_bh(&pipe_info->recv_bufs_needed_lock);
+ HIF_ERROR(
+ "%s buf alloc error [%d] needed %d, nbuf_alloc_err_count = %u",
+ __func__, pipe_info->pipe_num,
+ atomic_read(&pipe_info->recv_bufs_needed),
+ pipe_info->nbuf_ce_enqueue_err_count);
+ atomic_inc(&pipe_info->recv_bufs_needed);
+ cdf_nbuf_free(nbuf);
+ return 1;
+ }
+
+ cdf_spin_lock_bh(&pipe_info->recv_bufs_needed_lock);
+ bufs_posted++;
+ }
+ pipe_info->nbuf_alloc_err_count =
+ (pipe_info->nbuf_alloc_err_count > bufs_posted)?
+ pipe_info->nbuf_alloc_err_count - bufs_posted : 0;
+ pipe_info->nbuf_dma_err_count =
+ (pipe_info->nbuf_dma_err_count > bufs_posted)?
+ pipe_info->nbuf_dma_err_count - bufs_posted : 0;
+ pipe_info->nbuf_ce_enqueue_err_count =
+ (pipe_info->nbuf_ce_enqueue_err_count > bufs_posted)?
+ pipe_info->nbuf_ce_enqueue_err_count - bufs_posted : 0;
+
+ cdf_spin_unlock_bh(&pipe_info->recv_bufs_needed_lock);
+
+ return 0;
+}
+
+/*
+ * Try to post all desired receive buffers for all pipes.
+ * Returns 0 if all desired buffers are posted,
+ * non-zero if were were unable to completely
+ * replenish receive buffers.
+ */
+static int hif_post_recv_buffers(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ int pipe_num, rv = 0;
+
+ A_TARGET_ACCESS_LIKELY(scn);
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ struct HIF_CE_pipe_info *pipe_info;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ if (hif_post_recv_buffers_for_pipe(pipe_info)) {
+ rv = 1;
+ goto done;
+ }
+ }
+
+done:
+ A_TARGET_ACCESS_UNLIKELY(scn);
+
+ return rv;
+}
+
+CDF_STATUS hif_start(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ if (hif_completion_thread_startup(hif_state))
+ return CDF_STATUS_E_FAILURE;
+
+ hif_msg_callbacks_install(scn);
+
+ /* Post buffers once to start things off. */
+ (void)hif_post_recv_buffers(scn);
+
+ hif_state->started = true;
+
+ return CDF_STATUS_SUCCESS;
+}
+
+#ifdef WLAN_FEATURE_FASTPATH
+/**
+ * hif_enable_fastpath() Update that we have enabled fastpath mode
+ * @hif_device: HIF context
+ *
+ * For use in data path
+ *
+ * Retrun: void
+ */
+void
+hif_enable_fastpath(struct ol_softc *hif_device)
+{
+ HIF_INFO("Enabling fastpath mode\n");
+ hif_device->fastpath_mode_on = 1;
+}
+#endif /* WLAN_FEATURE_FASTPATH */
+
+void hif_recv_buffer_cleanup_on_pipe(struct HIF_CE_pipe_info *pipe_info)
+{
+ struct ol_softc *scn;
+ struct CE_handle *ce_hdl;
+ uint32_t buf_sz;
+ struct HIF_CE_state *hif_state;
+ cdf_nbuf_t netbuf;
+ cdf_dma_addr_t CE_data;
+ void *per_CE_context;
+
+ buf_sz = pipe_info->buf_sz;
+ if (buf_sz == 0) {
+ /* Unused Copy Engine */
+ return;
+ }
+
+ hif_state = pipe_info->HIF_CE_state;
+ if (!hif_state->started) {
+ return;
+ }
+
+ scn = hif_state->scn;
+ ce_hdl = pipe_info->ce_hdl;
+
+ if (scn->cdf_dev == NULL) {
+ return;
+ }
+ while (ce_revoke_recv_next
+ (ce_hdl, &per_CE_context, (void **)&netbuf,
+ &CE_data) == CDF_STATUS_SUCCESS) {
+ cdf_nbuf_unmap_single(scn->cdf_dev, netbuf,
+ CDF_DMA_FROM_DEVICE);
+ cdf_nbuf_free(netbuf);
+ }
+}
+
+void hif_send_buffer_cleanup_on_pipe(struct HIF_CE_pipe_info *pipe_info)
+{
+ struct CE_handle *ce_hdl;
+ struct HIF_CE_state *hif_state;
+ cdf_nbuf_t netbuf;
+ void *per_CE_context;
+ cdf_dma_addr_t CE_data;
+ unsigned int nbytes;
+ unsigned int id;
+ uint32_t buf_sz;
+ uint32_t toeplitz_hash_result;
+
+ buf_sz = pipe_info->buf_sz;
+ if (buf_sz == 0) {
+ /* Unused Copy Engine */
+ return;
+ }
+
+ hif_state = pipe_info->HIF_CE_state;
+ if (!hif_state->started) {
+ return;
+ }
+
+ ce_hdl = pipe_info->ce_hdl;
+
+ while (ce_cancel_send_next
+ (ce_hdl, &per_CE_context,
+ (void **)&netbuf, &CE_data, &nbytes,
+ &id, &toeplitz_hash_result) == CDF_STATUS_SUCCESS) {
+ if (netbuf != CE_SENDLIST_ITEM_CTXT) {
+ /*
+ * Packets enqueued by htt_h2t_ver_req_msg() and
+ * htt_h2t_rx_ring_cfg_msg_ll() have already been
+ * freed in htt_htc_misc_pkt_pool_free() in
+ * wlantl_close(), so do not free them here again
+ * by checking whether it's the EndPoint
+ * which they are queued in.
+ */
+ if (id == hif_state->scn->htc_endpoint)
+ return;
+ /* Indicate the completion to higer
+ * layer to free the buffer */
+ hif_state->msg_callbacks_current.
+ txCompletionHandler(hif_state->
+ msg_callbacks_current.Context,
+ netbuf, id, toeplitz_hash_result);
+ }
+ }
+}
+
+/*
+ * Cleanup residual buffers for device shutdown:
+ * buffers that were enqueued for receive
+ * buffers that were to be sent
+ * Note: Buffers that had completed but which were
+ * not yet processed are on a completion queue. They
+ * are handled when the completion thread shuts down.
+ */
+void hif_buffer_cleanup(struct HIF_CE_state *hif_state)
+{
+ int pipe_num;
+
+ for (pipe_num = 0; pipe_num < hif_state->scn->ce_count; pipe_num++) {
+ struct HIF_CE_pipe_info *pipe_info;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ hif_recv_buffer_cleanup_on_pipe(pipe_info);
+ hif_send_buffer_cleanup_on_pipe(pipe_info);
+ }
+}
+
+void hif_flush_surprise_remove(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ hif_buffer_cleanup(hif_state);
+}
+
+void hif_stop(struct ol_softc *scn)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ int pipe_num;
+
+ scn->hif_init_done = false;
+ if (hif_state->started) {
+ /* sync shutdown */
+ hif_completion_thread_shutdown(hif_state);
+ hif_completion_thread(hif_state);
+ } else {
+ hif_completion_thread_shutdown(hif_state);
+ }
+
+ /*
+ * At this point, asynchronous threads are stopped,
+ * The Target should not DMA nor interrupt, Host code may
+ * not initiate anything more. So we just need to clean
+ * up Host-side state.
+ */
+
+ if (scn->athdiag_procfs_inited) {
+ athdiag_procfs_remove();
+ scn->athdiag_procfs_inited = false;
+ }
+
+ hif_buffer_cleanup(hif_state);
+
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ struct HIF_CE_pipe_info *pipe_info;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ if (pipe_info->ce_hdl) {
+ ce_fini(pipe_info->ce_hdl);
+ pipe_info->ce_hdl = NULL;
+ pipe_info->buf_sz = 0;
+ }
+ }
+
+ if (hif_state->sleep_timer_init) {
+ cdf_softirq_timer_cancel(&hif_state->sleep_timer);
+ cdf_softirq_timer_free(&hif_state->sleep_timer);
+ hif_state->sleep_timer_init = false;
+ }
+
+ hif_state->started = false;
+}
+
+#define ADRASTEA_SRC_WR_INDEX_OFFSET 0x3C
+#define ADRASTEA_DST_WR_INDEX_OFFSET 0x40
+
+
+static struct shadow_reg_cfg target_shadow_reg_cfg_map[] = {
+ { 0, ADRASTEA_SRC_WR_INDEX_OFFSET},
+ { 3, ADRASTEA_SRC_WR_INDEX_OFFSET},
+ { 4, ADRASTEA_SRC_WR_INDEX_OFFSET},
+ { 5, ADRASTEA_SRC_WR_INDEX_OFFSET},
+ { 7, ADRASTEA_SRC_WR_INDEX_OFFSET},
+ { 1, ADRASTEA_DST_WR_INDEX_OFFSET},
+ { 2, ADRASTEA_DST_WR_INDEX_OFFSET},
+ { 7, ADRASTEA_DST_WR_INDEX_OFFSET},
+ { 8, ADRASTEA_DST_WR_INDEX_OFFSET},
+};
+
+
+
+/* CE_PCI TABLE */
+/*
+ * NOTE: the table below is out of date, though still a useful reference.
+ * Refer to target_service_to_ce_map and hif_map_service_to_pipe for the actual
+ * mapping of HTC services to HIF pipes.
+ */
+/*
+ * This authoritative table defines Copy Engine configuration and the mapping
+ * of services/endpoints to CEs. A subset of this information is passed to
+ * the Target during startup as a prerequisite to entering BMI phase.
+ * See:
+ * target_service_to_ce_map - Target-side mapping
+ * hif_map_service_to_pipe - Host-side mapping
+ * target_ce_config - Target-side configuration
+ * host_ce_config - Host-side configuration
+ ============================================================================
+ Purpose | Service / Endpoint | CE | Dire | Xfer | Xfer
+ | | | ctio | Size | Frequency
+ | | | n | |
+ ============================================================================
+ tx | HTT_DATA (downlink) | CE 0 | h->t | medium - | very frequent
+ descriptor | | | | O(100B) | and regular
+ download | | | | |
+ ----------------------------------------------------------------------------
+ rx | HTT_DATA (uplink) | CE 1 | t->h | small - | frequent and
+ indication | | | | O(10B) | regular
+ upload | | | | |
+ ----------------------------------------------------------------------------
+ MSDU | DATA_BK (uplink) | CE 2 | t->h | large - | rare
+ upload | | | | O(1000B) | (frequent
+ e.g. noise | | | | | during IP1.0
+ packets | | | | | testing)
+ ----------------------------------------------------------------------------
+ MSDU | DATA_BK (downlink) | CE 3 | h->t | large - | very rare
+ download | | | | O(1000B) | (frequent
+ e.g. | | | | | during IP1.0
+ misdirecte | | | | | testing)
+ d EAPOL | | | | |
+ packets | | | | |
+ ----------------------------------------------------------------------------
+ n/a | DATA_BE, DATA_VI | CE 2 | t->h | | never(?)
+ | DATA_VO (uplink) | | | |
+ ----------------------------------------------------------------------------
+ n/a | DATA_BE, DATA_VI | CE 3 | h->t | | never(?)
+ | DATA_VO (downlink) | | | |
+ ----------------------------------------------------------------------------
+ WMI events | WMI_CONTROL (uplink) | CE 4 | t->h | medium - | infrequent
+ | | | | O(100B) |
+ ----------------------------------------------------------------------------
+ WMI | WMI_CONTROL | CE 5 | h->t | medium - | infrequent
+ messages | (downlink) | | | O(100B) |
+ | | | | |
+ ----------------------------------------------------------------------------
+ n/a | HTC_CTRL_RSVD, | CE 1 | t->h | | never(?)
+ | HTC_RAW_STREAMS | | | |
+ | (uplink) | | | |
+ ----------------------------------------------------------------------------
+ n/a | HTC_CTRL_RSVD, | CE 0 | h->t | | never(?)
+ | HTC_RAW_STREAMS | | | |
+ | (downlink) | | | |
+ ----------------------------------------------------------------------------
+ diag | none (raw CE) | CE 7 | t<>h | 4 | Diag Window
+ | | | | | infrequent
+ ============================================================================
+ */
+
+/*
+ * Map from service/endpoint to Copy Engine.
+ * This table is derived from the CE_PCI TABLE, above.
+ * It is passed to the Target at startup for use by firmware.
+ */
+static struct service_to_pipe target_service_to_ce_map_wlan[] = {
+ {
+ WMI_DATA_VO_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 3,
+ },
+ {
+ WMI_DATA_VO_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ WMI_DATA_BK_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 3,
+ },
+ {
+ WMI_DATA_BK_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ WMI_DATA_BE_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 3,
+ },
+ {
+ WMI_DATA_BE_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ WMI_DATA_VI_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 3,
+ },
+ {
+ WMI_DATA_VI_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ WMI_CONTROL_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 3,
+ },
+ {
+ WMI_CONTROL_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ HTC_CTRL_RSVD_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 0, /* could be moved to 3 (share with WMI) */
+ },
+ {
+ HTC_CTRL_RSVD_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ HTC_RAW_STREAMS_SVC, /* not currently used */
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 0,
+ },
+ {
+ HTC_RAW_STREAMS_SVC, /* not currently used */
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 2,
+ },
+ {
+ HTT_DATA_MSG_SVC,
+ PIPEDIR_OUT, /* out = UL = host -> target */
+ 4,
+ },
+ {
+ HTT_DATA_MSG_SVC,
+ PIPEDIR_IN, /* in = DL = target -> host */
+ 1,
+ },
+ {
+ WDI_IPA_TX_SVC,
+ PIPEDIR_OUT, /* in = DL = target -> host */
+ 5,
+ },
+ /* (Additions here) */
+
+ { /* Must be last */
+ 0,
+ 0,
+ 0,
+ },
+};
+
+static struct service_to_pipe *target_service_to_ce_map =
+ target_service_to_ce_map_wlan;
+static int target_service_to_ce_map_sz = sizeof(target_service_to_ce_map_wlan);
+
+static struct shadow_reg_cfg *target_shadow_reg_cfg = target_shadow_reg_cfg_map;
+static int shadow_cfg_sz = sizeof(target_shadow_reg_cfg_map);
+
+static struct service_to_pipe target_service_to_ce_map_wlan_epping[] = {
+ {WMI_DATA_VO_SVC, PIPEDIR_OUT, 3,}, /* out = UL = host -> target */
+ {WMI_DATA_VO_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {WMI_DATA_BK_SVC, PIPEDIR_OUT, 4,}, /* out = UL = host -> target */
+ {WMI_DATA_BK_SVC, PIPEDIR_IN, 1,}, /* in = DL = target -> host */
+ {WMI_DATA_BE_SVC, PIPEDIR_OUT, 3,}, /* out = UL = host -> target */
+ {WMI_DATA_BE_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {WMI_DATA_VI_SVC, PIPEDIR_OUT, 3,}, /* out = UL = host -> target */
+ {WMI_DATA_VI_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {WMI_CONTROL_SVC, PIPEDIR_OUT, 3,}, /* out = UL = host -> target */
+ {WMI_CONTROL_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {HTC_CTRL_RSVD_SVC, PIPEDIR_OUT, 0,}, /* out = UL = host -> target */
+ {HTC_CTRL_RSVD_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {HTC_RAW_STREAMS_SVC, PIPEDIR_OUT, 0,}, /* out = UL = host -> target */
+ {HTC_RAW_STREAMS_SVC, PIPEDIR_IN, 2,}, /* in = DL = target -> host */
+ {HTT_DATA_MSG_SVC, PIPEDIR_OUT, 4,}, /* out = UL = host -> target */
+ {HTT_DATA_MSG_SVC, PIPEDIR_IN, 1,}, /* in = DL = target -> host */
+ {0, 0, 0,}, /* Must be last */
+};
+
+#ifdef HIF_PCI
+/*
+ * Send an interrupt to the device to wake up the Target CPU
+ * so it has an opportunity to notice any changed state.
+ */
+void hif_wake_target_cpu(struct ol_softc *scn)
+{
+ CDF_STATUS rv;
+ uint32_t core_ctrl;
+
+ rv = hif_diag_read_access(scn,
+ SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS,
+ &core_ctrl);
+ CDF_ASSERT(rv == CDF_STATUS_SUCCESS);
+ /* A_INUM_FIRMWARE interrupt to Target CPU */
+ core_ctrl |= CORE_CTRL_CPU_INTR_MASK;
+
+ rv = hif_diag_write_access(scn,
+ SOC_CORE_BASE_ADDRESS | CORE_CTRL_ADDRESS,
+ core_ctrl);
+ CDF_ASSERT(rv == CDF_STATUS_SUCCESS);
+}
+#endif
+
+static void hif_sleep_entry(void *arg)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)arg;
+ struct ol_softc *scn = hif_state->scn;
+ uint32_t idle_ms;
+ if (scn->recovery)
+ return;
+
+ cdf_spin_lock_irqsave(&hif_state->keep_awake_lock);
+ if (hif_state->verified_awake == false) {
+ idle_ms = cdf_system_ticks_to_msecs(cdf_system_ticks()
+ - hif_state->sleep_ticks);
+ if (idle_ms >= HIF_MIN_SLEEP_INACTIVITY_TIME_MS) {
+ if (!cdf_atomic_read(&scn->link_suspended)) {
+ soc_wake_reset(scn);
+ hif_state->fake_sleep = false;
+ }
+ } else {
+ cdf_softirq_timer_cancel(&hif_state->sleep_timer);
+ cdf_softirq_timer_start(&hif_state->sleep_timer,
+ HIF_SLEEP_INACTIVITY_TIMER_PERIOD_MS);
+ }
+ } else {
+ cdf_softirq_timer_cancel(&hif_state->sleep_timer);
+ cdf_softirq_timer_start(&hif_state->sleep_timer,
+ HIF_SLEEP_INACTIVITY_TIMER_PERIOD_MS);
+ }
+ cdf_spin_unlock_irqrestore(&hif_state->keep_awake_lock);
+}
+#define HIF_HIA_MAX_POLL_LOOP 1000000
+#define HIF_HIA_POLLING_DELAY_MS 10
+
+#ifndef HIF_PCI
+int hif_set_hia(struct ol_softc *scn)
+{
+ return 0;
+}
+#else
+int hif_set_hia(struct ol_softc *scn)
+{
+ CDF_STATUS rv;
+ uint32_t interconnect_targ_addr = 0;
+ uint32_t pcie_state_targ_addr = 0;
+ uint32_t pipe_cfg_targ_addr = 0;
+ uint32_t svc_to_pipe_map = 0;
+ uint32_t pcie_config_flags = 0;
+ uint32_t flag2_value = 0;
+ uint32_t flag2_targ_addr = 0;
+#ifdef QCA_WIFI_3_0
+ uint32_t host_interest_area = 0;
+ uint8_t i;
+#else
+ uint32_t ealloc_value = 0;
+ uint32_t ealloc_targ_addr = 0;
+ uint8_t banks_switched = 1;
+ uint32_t chip_id;
+#endif
+ uint32_t pipe_cfg_addr;
+
+ HIF_TRACE("%s: E", __func__);
+
+ if (IHELIUM_BU || ADRASTEA_BU)
+ return CDF_STATUS_SUCCESS;
+
+#ifdef QCA_WIFI_3_0
+ i = 0;
+ while (i < HIF_HIA_MAX_POLL_LOOP) {
+ host_interest_area = hif_read32_mb(scn->mem +
+ A_SOC_CORE_SCRATCH_0_ADDRESS);
+ if ((host_interest_area & 0x01) == 0) {
+ cdf_mdelay(HIF_HIA_POLLING_DELAY_MS);
+ host_interest_area = 0;
+ i++;
+ if (i > HIF_HIA_MAX_POLL_LOOP && (i % 1000 == 0)) {
+ HIF_ERROR("%s: poll timeout(%d)", __func__, i);
+ }
+ } else {
+ host_interest_area &= (~0x01);
+ hif_write32_mb(scn->mem + 0x113014, 0);
+ break;
+ }
+ }
+
+ if (i >= HIF_HIA_MAX_POLL_LOOP) {
+ HIF_ERROR("%s: hia polling timeout", __func__);
+ return -EIO;
+ }
+
+ if (host_interest_area == 0) {
+ HIF_ERROR("%s: host_interest_area = 0", __func__);
+ return -EIO;
+ }
+
+ interconnect_targ_addr = host_interest_area +
+ offsetof(struct host_interest_area_t,
+ hi_interconnect_state);
+
+ flag2_targ_addr = host_interest_area +
+ offsetof(struct host_interest_area_t, hi_option_flag2);
+
+#else
+ interconnect_targ_addr = hif_hia_item_address(scn->target_type,
+ offsetof(struct host_interest_s, hi_interconnect_state));
+ ealloc_targ_addr = hif_hia_item_address(scn->target_type,
+ offsetof(struct host_interest_s, hi_early_alloc));
+ flag2_targ_addr = hif_hia_item_address(scn->target_type,
+ offsetof(struct host_interest_s, hi_option_flag2));
+#endif
+ /* Supply Target-side CE configuration */
+ rv = hif_diag_read_access(scn, interconnect_targ_addr,
+ &pcie_state_targ_addr);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: interconnect_targ_addr = 0x%0x, ret = %d",
+ __func__, interconnect_targ_addr, rv);
+ goto done;
+ }
+ if (pcie_state_targ_addr == 0) {
+ rv = CDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: pcie state addr is 0", __func__);
+ goto done;
+ }
+ pipe_cfg_addr = pcie_state_targ_addr +
+ offsetof(struct pcie_state_s,
+ pipe_cfg_addr);
+ rv = hif_diag_read_access(scn,
+ pipe_cfg_addr,
+ &pipe_cfg_targ_addr);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: pipe_cfg_addr = 0x%0x, ret = %d",
+ __func__, pipe_cfg_addr, rv);
+ goto done;
+ }
+ if (pipe_cfg_targ_addr == 0) {
+ rv = CDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: pipe cfg addr is 0", __func__);
+ goto done;
+ }
+
+ rv = hif_diag_write_mem(scn, pipe_cfg_targ_addr,
+ (uint8_t *) target_ce_config,
+ target_ce_config_sz);
+
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: write pipe cfg (%d)", __func__, rv);
+ goto done;
+ }
+
+ rv = hif_diag_read_access(scn,
+ pcie_state_targ_addr +
+ offsetof(struct pcie_state_s,
+ svc_to_pipe_map),
+ &svc_to_pipe_map);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: get svc/pipe map (%d)", __func__, rv);
+ goto done;
+ }
+ if (svc_to_pipe_map == 0) {
+ rv = CDF_STATUS_E_FAILURE;
+ HIF_ERROR("%s: svc_to_pipe map is 0", __func__);
+ goto done;
+ }
+
+ rv = hif_diag_write_mem(scn,
+ svc_to_pipe_map,
+ (uint8_t *) target_service_to_ce_map,
+ target_service_to_ce_map_sz);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: write svc/pipe map (%d)", __func__, rv);
+ goto done;
+ }
+
+ rv = hif_diag_read_access(scn,
+ pcie_state_targ_addr +
+ offsetof(struct pcie_state_s,
+ config_flags),
+ &pcie_config_flags);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: get pcie config_flags (%d)", __func__, rv);
+ goto done;
+ }
+#if (CONFIG_PCIE_ENABLE_L1_CLOCK_GATE)
+ pcie_config_flags |= PCIE_CONFIG_FLAG_ENABLE_L1;
+#else
+ pcie_config_flags &= ~PCIE_CONFIG_FLAG_ENABLE_L1;
+#endif /* CONFIG_PCIE_ENABLE_L1_CLOCK_GATE */
+ pcie_config_flags |= PCIE_CONFIG_FLAG_CLK_SWITCH_WAIT;
+#if (CONFIG_PCIE_ENABLE_AXI_CLK_GATE)
+ pcie_config_flags |= PCIE_CONFIG_FLAG_AXI_CLK_GATE;
+#endif
+ rv = hif_diag_write_mem(scn,
+ pcie_state_targ_addr +
+ offsetof(struct pcie_state_s,
+ config_flags),
+ (uint8_t *) &pcie_config_flags,
+ sizeof(pcie_config_flags));
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: write pcie config_flags (%d)", __func__, rv);
+ goto done;
+ }
+
+#ifndef QCA_WIFI_3_0
+ /* configure early allocation */
+ ealloc_targ_addr = hif_hia_item_address(scn->target_type,
+ offsetof(
+ struct host_interest_s,
+ hi_early_alloc));
+
+ rv = hif_diag_read_access(scn, ealloc_targ_addr,
+ &ealloc_value);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: get early alloc val (%d)", __func__, rv);
+ goto done;
+ }
+
+ /* 1 bank is switched to IRAM, except ROME 1.0 */
+ ealloc_value |=
+ ((HI_EARLY_ALLOC_MAGIC << HI_EARLY_ALLOC_MAGIC_SHIFT) &
+ HI_EARLY_ALLOC_MAGIC_MASK);
+
+ rv = hif_diag_read_access(scn,
+ CHIP_ID_ADDRESS |
+ RTC_SOC_BASE_ADDRESS, &chip_id);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: get chip id val (%d)", __func__, rv);
+ goto done;
+ }
+ if (CHIP_ID_VERSION_GET(chip_id) == 0xD) {
+ scn->target_revision =
+ CHIP_ID_REVISION_GET(chip_id);
+ switch (CHIP_ID_REVISION_GET(chip_id)) {
+ case 0x2: /* ROME 1.3 */
+ /* 2 banks are switched to IRAM */
+ banks_switched = 2;
+ break;
+ case 0x4: /* ROME 2.1 */
+ case 0x5: /* ROME 2.2 */
+ banks_switched = 6;
+ break;
+ case 0x8: /* ROME 3.0 */
+ case 0x9: /* ROME 3.1 */
+ case 0xA: /* ROME 3.2 */
+ banks_switched = 9;
+ break;
+ case 0x0: /* ROME 1.0 */
+ case 0x1: /* ROME 1.1 */
+ default:
+ /* 3 banks are switched to IRAM */
+ banks_switched = 3;
+ break;
+ }
+ }
+
+ ealloc_value |=
+ ((banks_switched << HI_EARLY_ALLOC_IRAM_BANKS_SHIFT)
+ & HI_EARLY_ALLOC_IRAM_BANKS_MASK);
+
+ rv = hif_diag_write_access(scn,
+ ealloc_targ_addr,
+ ealloc_value);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: set early alloc val (%d)", __func__, rv);
+ goto done;
+ }
+#endif
+
+ /* Tell Target to proceed with initialization */
+ flag2_targ_addr = hif_hia_item_address(scn->target_type,
+ offsetof(
+ struct host_interest_s,
+ hi_option_flag2));
+
+ rv = hif_diag_read_access(scn, flag2_targ_addr,
+ &flag2_value);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: get option val (%d)", __func__, rv);
+ goto done;
+ }
+
+ flag2_value |= HI_OPTION_EARLY_CFG_DONE;
+ rv = hif_diag_write_access(scn, flag2_targ_addr,
+ flag2_value);
+ if (rv != CDF_STATUS_SUCCESS) {
+ HIF_ERROR("%s: set option val (%d)", __func__, rv);
+ goto done;
+ }
+
+ hif_wake_target_cpu(scn);
+
+done:
+
+ return rv;
+}
+#endif
+
+/**
+ * hif_wlan_enable(): call the platform driver to enable wlan
+ *
+ * This function passes the con_mode and CE configuration to
+ * platform driver to enable wlan.
+ *
+ * Return: void
+ */
+static int hif_wlan_enable(void)
+{
+ struct icnss_wlan_enable_cfg cfg;
+ enum icnss_driver_mode mode;
+ uint32_t con_mode = cds_get_conparam();
+
+ cfg.num_ce_tgt_cfg = target_ce_config_sz /
+ sizeof(struct CE_pipe_config);
+ cfg.ce_tgt_cfg = (struct ce_tgt_pipe_cfg *)target_ce_config;
+ cfg.num_ce_svc_pipe_cfg = target_service_to_ce_map_sz /
+ sizeof(struct service_to_pipe);
+ cfg.ce_svc_cfg = (struct ce_svc_pipe_cfg *)target_service_to_ce_map;
+ cfg.num_shadow_reg_cfg = shadow_cfg_sz / sizeof(struct shadow_reg_cfg);
+ cfg.shadow_reg_cfg = (struct icnss_shadow_reg_cfg *) target_shadow_reg_cfg;
+
+ switch (con_mode) {
+ case CDF_FTM_MODE:
+ mode = ICNSS_FTM;
+ break;
+ case CDF_EPPING_MODE:
+ mode = ICNSS_EPPING;
+ break;
+ default:
+ mode = ICNSS_MISSION;
+ break;
+ }
+ return icnss_wlan_enable(&cfg, mode, QWLAN_VERSIONSTR);
+}
+
+#if ((!defined(QCA_WIFI_3_0_IHELIUM) && !defined(QCA_WIFI_3_0_ADRASTEA)) || defined(CONFIG_ICNSS))
+static inline void cnss_pcie_notify_q6(void)
+{
+ return;
+}
+#endif
+
+/*
+ * Called from PCI layer whenever a new PCI device is probed.
+ * Initializes per-device HIF state and notifies the main
+ * driver that a new HIF device is present.
+ */
+int hif_config_ce(hif_handle_t hif_hdl)
+{
+ struct HIF_CE_state *hif_state;
+ struct HIF_CE_pipe_info *pipe_info;
+ int pipe_num;
+#ifdef ADRASTEA_SHADOW_REGISTERS
+ int i;
+#endif
+ CDF_STATUS rv = CDF_STATUS_SUCCESS;
+ int ret;
+ struct ol_softc *scn = hif_hdl;
+ struct icnss_soc_info soc_info;
+
+ /* if epping is enabled we need to use the epping configuration. */
+ if (WLAN_IS_EPPING_ENABLED(cds_get_conparam())) {
+ if (WLAN_IS_EPPING_IRQ(cds_get_conparam()))
+ host_ce_config = host_ce_config_wlan_epping_irq;
+ else
+ host_ce_config = host_ce_config_wlan_epping_poll;
+ target_ce_config = target_ce_config_wlan_epping;
+ target_ce_config_sz = sizeof(target_ce_config_wlan_epping);
+ target_service_to_ce_map =
+ target_service_to_ce_map_wlan_epping;
+ target_service_to_ce_map_sz =
+ sizeof(target_service_to_ce_map_wlan_epping);
+ }
+
+ ret = hif_wlan_enable();
+
+ if (ret) {
+ HIF_ERROR("%s: hif_wlan_enable error = %d", __func__, ret);
+ return CDF_STATUS_NOT_INITIALIZED;
+ }
+ if (IHELIUM_BU) {
+ cnss_pcie_notify_q6();
+ HIF_TRACE("%s: cnss_pcie_notify_q6 done, notice_send= %d",
+ __func__, scn->notice_send);
+ }
+
+ scn->notice_send = true;
+
+ cdf_mem_zero(&soc_info, sizeof(soc_info));
+ ret = icnss_get_soc_info(&soc_info);
+ if (ret < 0) {
+ HIF_ERROR("%s: icnss_get_soc_info error = %d", __func__, ret);
+ return CDF_STATUS_NOT_INITIALIZED;
+ }
+
+ hif_state = (struct HIF_CE_state *)cdf_mem_malloc(sizeof(*hif_state));
+ if (!hif_state) {
+ return -ENOMEM;
+ }
+ cdf_mem_zero(hif_state, sizeof(*hif_state));
+
+ hif_state->scn = scn;
+ scn->hif_hdl = hif_state;
+ scn->mem = soc_info.v_addr;
+ scn->mem_pa = soc_info.p_addr;
+ scn->soc_version = soc_info.version;
+
+ cdf_spinlock_init(&hif_state->keep_awake_lock);
+
+ cdf_atomic_init(&hif_state->hif_thread_idle);
+ cdf_atomic_inc(&hif_state->hif_thread_idle);
+
+ hif_state->keep_awake_count = 0;
+
+ hif_state->fake_sleep = false;
+ hif_state->sleep_ticks = 0;
+ cdf_softirq_timer_init(NULL, &hif_state->sleep_timer,
+ hif_sleep_entry, (void *)hif_state,
+ CDF_TIMER_TYPE_WAKE_APPS);
+ hif_state->sleep_timer_init = true;
+ hif_state->fw_indicator_address = FW_INDICATOR_ADDRESS;
+#ifdef HIF_PCI
+#if CONFIG_ATH_PCIE_MAX_PERF || CONFIG_ATH_PCIE_AWAKE_WHILE_DRIVER_LOAD
+ /* Force AWAKE forever/till the driver is loaded */
+ if (hif_target_sleep_state_adjust(scn, false, true) < 0)
+ return -EACCES;
+#endif
+#endif
+
+ /* During CE initializtion */
+ scn->ce_count = HOST_CE_COUNT;
+ A_TARGET_ACCESS_LIKELY(scn);
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ struct CE_attr *attr;
+
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ pipe_info->pipe_num = pipe_num;
+ pipe_info->HIF_CE_state = hif_state;
+ attr = &host_ce_config[pipe_num];
+ pipe_info->ce_hdl = ce_init(scn, pipe_num, attr);
+ CDF_ASSERT(pipe_info->ce_hdl != NULL);
+ if (pipe_info->ce_hdl == NULL) {
+ rv = CDF_STATUS_E_FAILURE;
+ A_TARGET_ACCESS_UNLIKELY(scn);
+ goto err;
+ }
+
+ if (pipe_num == DIAG_CE_ID) {
+ /* Reserve the ultimate CE for
+ * Diagnostic Window support */
+ hif_state->ce_diag =
+ hif_state->pipe_info[scn->ce_count - 1].ce_hdl;
+ continue;
+ }
+
+ pipe_info->buf_sz = (cdf_size_t) (attr->src_sz_max);
+ cdf_spinlock_init(&pipe_info->recv_bufs_needed_lock);
+ if (attr->dest_nentries > 0) {
+ atomic_set(&pipe_info->recv_bufs_needed,
+ init_buffer_count(attr->dest_nentries - 1));
+ } else {
+ atomic_set(&pipe_info->recv_bufs_needed, 0);
+ }
+ ce_tasklet_init(hif_state, (1 << pipe_num));
+ ce_register_irq(hif_state, (1 << pipe_num));
+ scn->request_irq_done = true;
+ }
+
+ if (athdiag_procfs_init(scn) != 0) {
+ A_TARGET_ACCESS_UNLIKELY(scn);
+ goto err;
+ }
+ scn->athdiag_procfs_inited = true;
+
+ /*
+ * Initially, establish CE completion handlers for use with BMI.
+ * These are overwritten with generic handlers after we exit BMI phase.
+ */
+ pipe_info = &hif_state->pipe_info[BMI_CE_NUM_TO_TARG];
+#ifdef HIF_PCI
+ ce_send_cb_register(
+ pipe_info->ce_hdl, hif_bmi_send_done, pipe_info, 0);
+#ifndef BMI_RSP_POLLING
+ pipe_info = &hif_state->pipe_info[BMI_CE_NUM_TO_HOST];
+ ce_recv_cb_register(
+ pipe_info->ce_hdl, hif_bmi_recv_data, pipe_info, 0);
+#endif
+#endif
+ HIF_INFO_MED("%s: ce_init done", __func__);
+
+ rv = hif_set_hia(scn);
+
+ HIF_INFO_MED("%s: hif_set_hia done", __func__);
+
+ A_TARGET_ACCESS_UNLIKELY(scn);
+
+ if (rv != CDF_STATUS_SUCCESS)
+ goto err;
+ else
+ init_tasklet_workers();
+
+ HIF_TRACE("%s: X, ret = %d\n", __func__, rv);
+
+#ifdef ADRASTEA_SHADOW_REGISTERS
+ HIF_ERROR("Using Shadow Registers instead of CE Registers\n");
+ for (i = 0; i < NUM_SHADOW_REGISTERS; i++) {
+ HIF_ERROR("%s Shadow Register%d is mapped to address %x\n",
+ __func__, i,
+ (A_TARGET_READ(scn, (SHADOW_ADDRESS(i))) << 2));
+ }
+#endif
+
+
+ return rv != CDF_STATUS_SUCCESS;
+
+err:
+ /* Failure, so clean up */
+ for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) {
+ pipe_info = &hif_state->pipe_info[pipe_num];
+ if (pipe_info->ce_hdl) {
+ ce_unregister_irq(hif_state, (1 << pipe_num));
+ scn->request_irq_done = false;
+ ce_fini(pipe_info->ce_hdl);
+ pipe_info->ce_hdl = NULL;
+ pipe_info->buf_sz = 0;
+ }
+ }
+ if (hif_state->sleep_timer_init) {
+ cdf_softirq_timer_cancel(&hif_state->sleep_timer);
+ cdf_softirq_timer_free(&hif_state->sleep_timer);
+ hif_state->sleep_timer_init = false;
+ }
+ if (scn->hif_hdl) {
+ scn->hif_hdl = NULL;
+ cdf_mem_free(hif_state);
+ }
+ athdiag_procfs_remove();
+ scn->athdiag_procfs_inited = false;
+ HIF_TRACE("%s: X, ret = %d\n", __func__, rv);
+ return CDF_STATUS_SUCCESS != CDF_STATUS_E_FAILURE;
+}
+
+
+
+
+
+
+#ifdef IPA_OFFLOAD
+void hif_ipa_get_ce_resource(struct ol_softc *scn,
+ uint32_t *ce_sr_base_paddr,
+ uint32_t *ce_sr_ring_size,
+ cdf_dma_addr_t *ce_reg_paddr)
+{
+ struct HIF_CE_state *hif_state = (struct HIF_CE_state *)scn->hif_hdl;
+ struct HIF_CE_pipe_info *pipe_info =
+ &(hif_state->pipe_info[HIF_PCI_IPA_UC_ASSIGNED_CE]);
+ struct CE_handle *ce_hdl = pipe_info->ce_hdl;
+
+ ce_ipa_get_resource(ce_hdl, ce_sr_base_paddr, ce_sr_ring_size,
+ ce_reg_paddr);
+ return;
+}
+#endif /* IPA_OFFLOAD */
+
+
+#ifdef ADRASTEA_SHADOW_REGISTERS
+
+/*
+ Current shadow register config
+
+ -----------------------------------------------------------
+ Shadow Register | CE | src/dst write index
+ -----------------------------------------------------------
+ 0 | 0 | src
+ 1 No Config - Doesn't point to anything
+ 2 No Config - Doesn't point to anything
+ 3 | 3 | src
+ 4 | 4 | src
+ 5 | 5 | src
+ 6 No Config - Doesn't point to anything
+ 7 | 7 | src
+ 8 No Config - Doesn't point to anything
+ 9 No Config - Doesn't point to anything
+ 10 No Config - Doesn't point to anything
+ 11 No Config - Doesn't point to anything
+ -----------------------------------------------------------
+ 12 No Config - Doesn't point to anything
+ 13 | 1 | dst
+ 14 | 2 | dst
+ 15 No Config - Doesn't point to anything
+ 16 No Config - Doesn't point to anything
+ 17 No Config - Doesn't point to anything
+ 18 No Config - Doesn't point to anything
+ 19 | 7 | dst
+ 20 | 8 | dst
+ 21 No Config - Doesn't point to anything
+ 22 No Config - Doesn't point to anything
+ 23 No Config - Doesn't point to anything
+ -----------------------------------------------------------
+
+
+ ToDo - Move shadow register config to following in the future
+ This helps free up a block of shadow registers towards the end.
+ Can be used for other purposes
+
+ -----------------------------------------------------------
+ Shadow Register | CE | src/dst write index
+ -----------------------------------------------------------
+ 0 | 0 | src
+ 1 | 3 | src
+ 2 | 4 | src
+ 3 | 5 | src
+ 4 | 7 | src
+ -----------------------------------------------------------
+ 5 | 1 | dst
+ 6 | 2 | dst
+ 7 | 7 | dst
+ 8 | 8 | dst
+ -----------------------------------------------------------
+ 9 No Config - Doesn't point to anything
+ 12 No Config - Doesn't point to anything
+ 13 No Config - Doesn't point to anything
+ 14 No Config - Doesn't point to anything
+ 15 No Config - Doesn't point to anything
+ 16 No Config - Doesn't point to anything
+ 17 No Config - Doesn't point to anything
+ 18 No Config - Doesn't point to anything
+ 19 No Config - Doesn't point to anything
+ 20 No Config - Doesn't point to anything
+ 21 No Config - Doesn't point to anything
+ 22 No Config - Doesn't point to anything
+ 23 No Config - Doesn't point to anything
+ -----------------------------------------------------------
+*/
+
+u32 shadow_sr_wr_ind_addr(struct ol_softc *scn, u32 ctrl_addr)
+{
+ u32 addr = 0;
+
+ switch (COPY_ENGINE_ID(ctrl_addr)) {
+ case 0:
+ addr = SHADOW_VALUE0;
+ break;
+ case 3:
+ addr = SHADOW_VALUE3;
+ break;
+ case 4:
+ addr = SHADOW_VALUE4;
+ break;
+ case 5:
+ addr = SHADOW_VALUE5;
+ break;
+ case 7:
+ addr = SHADOW_VALUE7;
+ break;
+ default:
+ printk("invalid CE ctrl_addr\n");
+ CDF_ASSERT(0);
+
+ }
+ return addr;
+
+}
+
+u32 shadow_dst_wr_ind_addr(struct ol_softc *scn, u32 ctrl_addr)
+{
+ u32 addr = 0;
+
+ switch (COPY_ENGINE_ID(ctrl_addr)) {
+ case 1:
+ addr = SHADOW_VALUE13;
+ break;
+ case 2:
+ addr = SHADOW_VALUE14;
+ break;
+ case 7:
+ addr = SHADOW_VALUE19;
+ break;
+ case 8:
+ addr = SHADOW_VALUE20;
+ break;
+ default:
+ printk("invalid CE ctrl_addr\n");
+ CDF_ASSERT(0);
+ }
+
+ return addr;
+
+}
+#endif
+
+void ce_lro_flush_cb_register(struct ol_softc *scn,
+ void (handler)(void *), void *data)
+{
+ struct CE_state *ce_state = scn->ce_id_to_state[CE_HTT_T2H_MSG];
+ ce_state->lro_flush_cb = handler;
+ ce_state->lro_data = data;
+}
diff --git a/core/hif/src/ce/ce_main.h b/core/hif/src/ce/ce_main.h
new file mode 100644
index 0000000..570fd41
--- /dev/null
+++ b/core/hif/src/ce/ce_main.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef __CE_H__
+#define __CE_H__
+
+#include "cdf_atomic.h"
+#include "cdf_lock.h"
+#include "hif.h"
+
+#define CE_HTT_T2H_MSG 1
+#define CE_HTT_H2T_MSG 4
+
+/**
+ * enum ce_id_type
+ *
+ * @ce_id_type: Copy engine ID
+ */
+enum ce_id_type {
+ CE_ID_0,
+ CE_ID_1,
+ CE_ID_2,
+ CE_ID_3,
+ CE_ID_4,
+ CE_ID_5,
+ CE_ID_6,
+ CE_ID_7,
+ CE_ID_8,
+ CE_ID_9,
+ CE_ID_10,
+ CE_ID_11,
+ CE_ID_MAX
+};
+struct HIF_CE_completion_state {
+ struct HIF_CE_completion_state *next;
+ int send_or_recv;
+ struct CE_handle *copyeng;
+ void *ce_context;
+ void *transfer_context;
+ cdf_dma_addr_t data;
+ unsigned int nbytes;
+ unsigned int transfer_id;
+ unsigned int flags;
+ uint32_t toeplitz_hash_result;
+};
+
+/* compl_state.send_or_recv */
+#define HIF_CE_COMPLETE_FREE 0
+#define HIF_CE_COMPLETE_SEND 1
+#define HIF_CE_COMPLETE_RECV 2
+
+enum ol_ath_hif_pkt_ecodes {
+ HIF_PIPE_NO_RESOURCE = 0
+};
+
+struct HIF_CE_state;
+#define HIF_CE_COMPLETE_STATE_NUM 18 /* 56 * 18 + 4/8 = 1012/1016 bytes */
+struct HIF_CE_completion_state_list {
+ struct HIF_CE_completion_state_list *next;
+};
+
+/* Per-pipe state. */
+struct HIF_CE_pipe_info {
+ /* Handle of underlying Copy Engine */
+ struct CE_handle *ce_hdl;
+
+ /* Our pipe number; facilitiates use of pipe_info ptrs. */
+ uint8_t pipe_num;
+
+ /* Convenience back pointer to HIF_CE_state. */
+ struct HIF_CE_state *HIF_CE_state;
+
+ /* Instantaneous number of receive buffers that should be posted */
+ atomic_t recv_bufs_needed;
+ cdf_size_t buf_sz;
+ cdf_spinlock_t recv_bufs_needed_lock;
+
+ cdf_spinlock_t completion_freeq_lock;
+ /* Limit the number of outstanding send requests. */
+ int num_sends_allowed;
+ struct HIF_CE_completion_state_list *completion_space_list;
+ struct HIF_CE_completion_state *completion_freeq_head;
+ struct HIF_CE_completion_state *completion_freeq_tail;
+ /* adding three counts for debugging ring buffer errors */
+ uint32_t nbuf_alloc_err_count;
+ uint32_t nbuf_dma_err_count;
+ uint32_t nbuf_ce_enqueue_err_count;
+};
+
+/**
+ * struct ce_tasklet_entry
+ *
+ * @intr_tq: intr_tq
+ * @ce_id: ce_id
+ * @inited: inited
+ * @hif_ce_state: hif_ce_state
+ * @from_irq: from_irq
+ */
+struct ce_tasklet_entry {
+ struct tasklet_struct intr_tq;
+ enum ce_id_type ce_id;
+ bool inited;
+ void *hif_ce_state;
+ bool from_irq;
+};
+
+struct HIF_CE_state {
+ struct ol_softc *scn;
+ bool started;
+ struct ce_tasklet_entry tasklets[CE_COUNT_MAX];
+ cdf_spinlock_t keep_awake_lock;
+ unsigned int keep_awake_count;
+ bool verified_awake;
+ bool fake_sleep;
+ cdf_softirq_timer_t sleep_timer;
+ bool sleep_timer_init;
+ unsigned long sleep_ticks;
+ cdf_spinlock_t completion_pendingq_lock;
+ /* Queue of send/recv completions that need to be processed */
+ struct HIF_CE_completion_state *completion_pendingq_head;
+ struct HIF_CE_completion_state *completion_pendingq_tail;
+ atomic_t fw_event_pending;
+ cdf_atomic_t hif_thread_idle;
+
+ /* wait_queue_head_t service_waitq; */
+ /* struct task_struct *compl_thread; */
+ /* struct completion compl_thread_done; */
+
+ /* Per-pipe state. */
+ struct HIF_CE_pipe_info pipe_info[CE_COUNT_MAX];
+ /* to be activated after BMI_DONE */
+ struct hif_msg_callbacks msg_callbacks_pending;
+ /* current msg callbacks in use */
+ struct hif_msg_callbacks msg_callbacks_current;
+
+ void *claimedContext;
+
+ /* Target address used to signal a pending firmware event */
+ uint32_t fw_indicator_address;
+
+ /* Copy Engine used for Diagnostic Accesses */
+ struct CE_handle *ce_diag;
+};
+#endif /* __CE_H__ */
diff --git a/core/hif/src/ce/ce_reg.h b/core/hif/src/ce/ce_reg.h
new file mode 100644
index 0000000..cb74394
--- /dev/null
+++ b/core/hif/src/ce/ce_reg.h
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef __CE_REG_H__
+#define __CE_REG_H__
+
+#define DST_WR_INDEX_ADDRESS (scn->target_ce_def->d_DST_WR_INDEX_ADDRESS)
+#define SRC_WATERMARK_ADDRESS (scn->target_ce_def->d_SRC_WATERMARK_ADDRESS)
+#define SRC_WATERMARK_LOW_MASK (scn->target_ce_def->d_SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_HIGH_MASK (scn->target_ce_def->d_SRC_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_LOW_MASK (scn->target_ce_def->d_DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_HIGH_MASK (scn->target_ce_def->d_DST_WATERMARK_HIGH_MASK)
+#define CURRENT_SRRI_ADDRESS (scn->target_ce_def->d_CURRENT_SRRI_ADDRESS)
+#define CURRENT_DRRI_ADDRESS (scn->target_ce_def->d_CURRENT_DRRI_ADDRESS)
+
+#define SHADOW_VALUE0 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_0)
+#define SHADOW_VALUE1 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_1)
+#define SHADOW_VALUE2 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_2)
+#define SHADOW_VALUE3 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_3)
+#define SHADOW_VALUE4 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_4)
+#define SHADOW_VALUE5 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_5)
+#define SHADOW_VALUE6 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_6)
+#define SHADOW_VALUE7 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_7)
+#define SHADOW_VALUE8 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_8)
+#define SHADOW_VALUE9 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_9)
+#define SHADOW_VALUE10 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_10)
+#define SHADOW_VALUE11 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_11)
+#define SHADOW_VALUE12 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_12)
+#define SHADOW_VALUE13 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_13)
+#define SHADOW_VALUE14 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_14)
+#define SHADOW_VALUE15 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_15)
+#define SHADOW_VALUE16 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_16)
+#define SHADOW_VALUE17 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_17)
+#define SHADOW_VALUE18 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_18)
+#define SHADOW_VALUE19 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_19)
+#define SHADOW_VALUE20 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_20)
+#define SHADOW_VALUE21 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_21)
+#define SHADOW_VALUE22 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_22)
+#define SHADOW_VALUE23 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_VALUE_23)
+#define SHADOW_ADDRESS0 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_0)
+#define SHADOW_ADDRESS1 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_1)
+#define SHADOW_ADDRESS2 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_2)
+#define SHADOW_ADDRESS3 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_3)
+#define SHADOW_ADDRESS4 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_4)
+#define SHADOW_ADDRESS5 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_5)
+#define SHADOW_ADDRESS6 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_6)
+#define SHADOW_ADDRESS7 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_7)
+#define SHADOW_ADDRESS8 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_8)
+#define SHADOW_ADDRESS9 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_9)
+#define SHADOW_ADDRESS10 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_10)
+#define SHADOW_ADDRESS11 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_11)
+#define SHADOW_ADDRESS12 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_12)
+#define SHADOW_ADDRESS13 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_13)
+#define SHADOW_ADDRESS14 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_14)
+#define SHADOW_ADDRESS15 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_15)
+#define SHADOW_ADDRESS16 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_16)
+#define SHADOW_ADDRESS17 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_17)
+#define SHADOW_ADDRESS18 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_18)
+#define SHADOW_ADDRESS19 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_19)
+#define SHADOW_ADDRESS20 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_20)
+#define SHADOW_ADDRESS21 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_21)
+#define SHADOW_ADDRESS22 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_22)
+#define SHADOW_ADDRESS23 (scn->host_shadow_regs->d_A_LOCAL_SHADOW_REG_ADDRESS_23)
+
+#define SHADOW_ADDRESS(i) (SHADOW_ADDRESS0 + i*(SHADOW_ADDRESS1-SHADOW_ADDRESS0))
+
+#define HOST_IS_SRC_RING_HIGH_WATERMARK_MASK \
+ (scn->target_ce_def->d_HOST_IS_SRC_RING_HIGH_WATERMARK_MASK)
+#define HOST_IS_SRC_RING_LOW_WATERMARK_MASK \
+ (scn->target_ce_def->d_HOST_IS_SRC_RING_LOW_WATERMARK_MASK)
+#define HOST_IS_DST_RING_HIGH_WATERMARK_MASK \
+ (scn->target_ce_def->d_HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+#define HOST_IS_DST_RING_LOW_WATERMARK_MASK \
+ (scn->target_ce_def->d_HOST_IS_DST_RING_LOW_WATERMARK_MASK)
+#define MISC_IS_ADDRESS (scn->target_ce_def->d_MISC_IS_ADDRESS)
+#define HOST_IS_COPY_COMPLETE_MASK \
+ (scn->target_ce_def->d_HOST_IS_COPY_COMPLETE_MASK)
+#define CE_WRAPPER_BASE_ADDRESS (scn->target_ce_def->d_CE_WRAPPER_BASE_ADDRESS)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS \
+ (scn->target_ce_def->d_CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)
+#define CE_WRAPPER_INDEX_BASE_LOW \
+ (scn->target_ce_def->d_CE_WRAPPER_INDEX_BASE_LOW)
+#define CE_WRAPPER_INDEX_BASE_HIGH \
+ (scn->target_ce_def->d_CE_WRAPPER_INDEX_BASE_HIGH)
+#define HOST_IE_COPY_COMPLETE_MASK \
+ (scn->target_ce_def->d_HOST_IE_COPY_COMPLETE_MASK)
+#define SR_BA_ADDRESS (scn->target_ce_def->d_SR_BA_ADDRESS)
+#define SR_BA_ADDRESS_HIGH (scn->target_ce_def->d_SR_BA_ADDRESS_HIGH)
+#define SR_SIZE_ADDRESS (scn->target_ce_def->d_SR_SIZE_ADDRESS)
+#define CE_CTRL1_ADDRESS (scn->target_ce_def->d_CE_CTRL1_ADDRESS)
+#define CE_CTRL1_DMAX_LENGTH_MASK \
+ (scn->target_ce_def->d_CE_CTRL1_DMAX_LENGTH_MASK)
+#define DR_BA_ADDRESS (scn->target_ce_def->d_DR_BA_ADDRESS)
+#define DR_BA_ADDRESS_HIGH (scn->target_ce_def->d_DR_BA_ADDRESS_HIGH)
+#define DR_SIZE_ADDRESS (scn->target_ce_def->d_DR_SIZE_ADDRESS)
+#define CE_CMD_REGISTER (scn->target_ce_def->d_CE_CMD_REGISTER)
+#define CE_MSI_ADDRESS (scn->target_ce_def->d_CE_MSI_ADDRESS)
+#define CE_MSI_ADDRESS_HIGH (scn->target_ce_def->d_CE_MSI_ADDRESS_HIGH)
+#define CE_MSI_DATA (scn->target_ce_def->d_CE_MSI_DATA)
+#define CE_MSI_ENABLE_BIT (scn->target_ce_def->d_CE_MSI_ENABLE_BIT)
+#define MISC_IE_ADDRESS (scn->target_ce_def->d_MISC_IE_ADDRESS)
+#define MISC_IS_AXI_ERR_MASK (scn->target_ce_def->d_MISC_IS_AXI_ERR_MASK)
+#define MISC_IS_DST_ADDR_ERR_MASK \
+ (scn->target_ce_def->d_MISC_IS_DST_ADDR_ERR_MASK)
+#define MISC_IS_SRC_LEN_ERR_MASK \
+ (scn->target_ce_def->d_MISC_IS_SRC_LEN_ERR_MASK)
+#define MISC_IS_DST_MAX_LEN_VIO_MASK \
+ (scn->target_ce_def->d_MISC_IS_DST_MAX_LEN_VIO_MASK)
+#define MISC_IS_DST_RING_OVERFLOW_MASK \
+ (scn->target_ce_def->d_MISC_IS_DST_RING_OVERFLOW_MASK)
+#define MISC_IS_SRC_RING_OVERFLOW_MASK \
+ (scn->target_ce_def->d_MISC_IS_SRC_RING_OVERFLOW_MASK)
+#define SRC_WATERMARK_LOW_LSB (scn->target_ce_def->d_SRC_WATERMARK_LOW_LSB)
+#define SRC_WATERMARK_HIGH_LSB (scn->target_ce_def->d_SRC_WATERMARK_HIGH_LSB)
+#define DST_WATERMARK_LOW_LSB (scn->target_ce_def->d_DST_WATERMARK_LOW_LSB)
+#define DST_WATERMARK_HIGH_LSB (scn->target_ce_def->d_DST_WATERMARK_HIGH_LSB)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK \
+ (scn->target_ce_def->d_CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB \
+ (scn->target_ce_def->d_CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_CTRL1_DMAX_LENGTH_LSB (scn->target_ce_def->d_CE_CTRL1_DMAX_LENGTH_LSB)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK \
+ (scn->target_ce_def->d_CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK \
+ (scn->target_ce_def->d_CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB \
+ (scn->target_ce_def->d_CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB)
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB \
+ (scn->target_ce_def->d_CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB)
+#define WLAN_DEBUG_INPUT_SEL_OFFSET \
+ (scn->targetdef->d_WLAN_DEBUG_INPUT_SEL_OFFSET)
+#define WLAN_DEBUG_INPUT_SEL_SRC_MSB \
+ (scn->targetdef->d_WLAN_DEBUG_INPUT_SEL_SRC_MSB)
+#define WLAN_DEBUG_INPUT_SEL_SRC_LSB \
+ (scn->targetdef->d_WLAN_DEBUG_INPUT_SEL_SRC_LSB)
+#define WLAN_DEBUG_INPUT_SEL_SRC_MASK \
+ (scn->targetdef->d_WLAN_DEBUG_INPUT_SEL_SRC_MASK)
+#define WLAN_DEBUG_CONTROL_OFFSET (scn->targetdef->d_WLAN_DEBUG_CONTROL_OFFSET)
+#define WLAN_DEBUG_CONTROL_ENABLE_MSB \
+ (scn->targetdef->d_WLAN_DEBUG_CONTROL_ENABLE_MSB)
+#define WLAN_DEBUG_CONTROL_ENABLE_LSB \
+ (scn->targetdef->d_WLAN_DEBUG_CONTROL_ENABLE_LSB)
+#define WLAN_DEBUG_CONTROL_ENABLE_MASK \
+ (scn->targetdef->d_WLAN_DEBUG_CONTROL_ENABLE_MASK)
+#define WLAN_DEBUG_OUT_OFFSET (scn->targetdef->d_WLAN_DEBUG_OUT_OFFSET)
+#define WLAN_DEBUG_OUT_DATA_MSB (scn->targetdef->d_WLAN_DEBUG_OUT_DATA_MSB)
+#define WLAN_DEBUG_OUT_DATA_LSB (scn->targetdef->d_WLAN_DEBUG_OUT_DATA_LSB)
+#define WLAN_DEBUG_OUT_DATA_MASK (scn->targetdef->d_WLAN_DEBUG_OUT_DATA_MASK)
+#define AMBA_DEBUG_BUS_OFFSET (scn->targetdef->d_AMBA_DEBUG_BUS_OFFSET)
+#define AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MSB \
+ (scn->targetdef->d_AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MSB)
+#define AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_LSB \
+ (scn->targetdef->d_AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_LSB)
+#define AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MASK \
+ (scn->targetdef->d_AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MASK)
+#define AMBA_DEBUG_BUS_SEL_MSB (scn->targetdef->d_AMBA_DEBUG_BUS_SEL_MSB)
+#define AMBA_DEBUG_BUS_SEL_LSB (scn->targetdef->d_AMBA_DEBUG_BUS_SEL_LSB)
+#define AMBA_DEBUG_BUS_SEL_MASK (scn->targetdef->d_AMBA_DEBUG_BUS_SEL_MASK)
+#define CE_WRAPPER_DEBUG_OFFSET (scn->target_ce_def->d_CE_WRAPPER_DEBUG_OFFSET)
+#define CE_WRAPPER_DEBUG_SEL_MSB (scn->target_ce_def->d_CE_WRAPPER_DEBUG_SEL_MSB)
+#define CE_WRAPPER_DEBUG_SEL_LSB (scn->target_ce_def->d_CE_WRAPPER_DEBUG_SEL_LSB)
+#define CE_WRAPPER_DEBUG_SEL_MASK (scn->target_ce_def->d_CE_WRAPPER_DEBUG_SEL_MASK)
+#define CE_DEBUG_OFFSET (scn->target_ce_def->d_CE_DEBUG_OFFSET)
+#define CE_DEBUG_SEL_MSB (scn->target_ce_def->d_CE_DEBUG_SEL_MSB)
+#define CE_DEBUG_SEL_LSB (scn->target_ce_def->d_CE_DEBUG_SEL_LSB)
+#define CE_DEBUG_SEL_MASK (scn->target_ce_def->d_CE_DEBUG_SEL_MASK)
+#define HOST_IE_ADDRESS (scn->target_ce_def->d_HOST_IE_ADDRESS)
+#define HOST_IS_ADDRESS (scn->target_ce_def->d_HOST_IS_ADDRESS)
+
+#define SRC_WATERMARK_LOW_SET(x) \
+ (((x) << SRC_WATERMARK_LOW_LSB) & SRC_WATERMARK_LOW_MASK)
+#define SRC_WATERMARK_HIGH_SET(x) \
+ (((x) << SRC_WATERMARK_HIGH_LSB) & SRC_WATERMARK_HIGH_MASK)
+#define DST_WATERMARK_LOW_SET(x) \
+ (((x) << DST_WATERMARK_LOW_LSB) & DST_WATERMARK_LOW_MASK)
+#define DST_WATERMARK_HIGH_SET(x) \
+ (((x) << DST_WATERMARK_HIGH_LSB) & DST_WATERMARK_HIGH_MASK)
+#define CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET(x) \
+ (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \
+ CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB)
+#define CE_CTRL1_DMAX_LENGTH_SET(x) \
+ (((x) << CE_CTRL1_DMAX_LENGTH_LSB) & CE_CTRL1_DMAX_LENGTH_MASK)
+#define CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(x) \
+ (((x) << CE_CTRL1_SRC_RING_BYTE_SWAP_EN_LSB) & \
+ CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK)
+#define CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(x) \
+ (((x) << CE_CTRL1_DST_RING_BYTE_SWAP_EN_LSB) & \
+ CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK)
+#define WLAN_DEBUG_INPUT_SEL_SRC_GET(x) \
+ (((x) & WLAN_DEBUG_INPUT_SEL_SRC_MASK) >> \
+ WLAN_DEBUG_INPUT_SEL_SRC_LSB)
+#define WLAN_DEBUG_INPUT_SEL_SRC_SET(x) \
+ (((x) << WLAN_DEBUG_INPUT_SEL_SRC_LSB) & \
+ WLAN_DEBUG_INPUT_SEL_SRC_MASK)
+#define WLAN_DEBUG_CONTROL_ENABLE_GET(x) \
+ (((x) & WLAN_DEBUG_CONTROL_ENABLE_MASK) >> \
+ WLAN_DEBUG_CONTROL_ENABLE_LSB)
+#define WLAN_DEBUG_CONTROL_ENABLE_SET(x) \
+ (((x) << WLAN_DEBUG_CONTROL_ENABLE_LSB) & \
+ WLAN_DEBUG_CONTROL_ENABLE_MASK)
+#define WLAN_DEBUG_OUT_DATA_GET(x) \
+ (((x) & WLAN_DEBUG_OUT_DATA_MASK) >> WLAN_DEBUG_OUT_DATA_LSB)
+#define WLAN_DEBUG_OUT_DATA_SET(x) \
+ (((x) << WLAN_DEBUG_OUT_DATA_LSB) & WLAN_DEBUG_OUT_DATA_MASK)
+#define AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_GET(x) \
+ (((x) & AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MASK) >> \
+ AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_LSB)
+#define AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_SET(x) \
+ (((x) << AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_LSB) & \
+ AMBA_DEBUG_BUS_PCIE_DEBUG_SEL_MASK)
+#define AMBA_DEBUG_BUS_SEL_GET(x) \
+ (((x) & AMBA_DEBUG_BUS_SEL_MASK) >> AMBA_DEBUG_BUS_SEL_LSB)
+#define AMBA_DEBUG_BUS_SEL_SET(x) \
+ (((x) << AMBA_DEBUG_BUS_SEL_LSB) & AMBA_DEBUG_BUS_SEL_MASK)
+#define CE_WRAPPER_DEBUG_SEL_GET(x) \
+ (((x) & CE_WRAPPER_DEBUG_SEL_MASK) >> CE_WRAPPER_DEBUG_SEL_LSB)
+#define CE_WRAPPER_DEBUG_SEL_SET(x) \
+ (((x) << CE_WRAPPER_DEBUG_SEL_LSB) & CE_WRAPPER_DEBUG_SEL_MASK)
+#define CE_DEBUG_SEL_GET(x) (((x) & CE_DEBUG_SEL_MASK) >> CE_DEBUG_SEL_LSB)
+#define CE_DEBUG_SEL_SET(x) (((x) << CE_DEBUG_SEL_LSB) & CE_DEBUG_SEL_MASK)
+
+#define CE_SRC_RING_READ_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + CURRENT_SRRI_ADDRESS)
+
+#define CE_SRC_RING_BASE_ADDR_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SR_BA_ADDRESS, (addr))
+
+#define CE_SRC_RING_BASE_ADDR_HIGH_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SR_BA_ADDRESS_HIGH, (addr))
+
+#define CE_SRC_RING_BASE_ADDR_HIGH_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + SR_BA_ADDRESS_HIGH)
+
+#define CE_SRC_RING_SZ_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SR_SIZE_ADDRESS, (n))
+
+#define CE_SRC_RING_DMAX_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_CTRL1_ADDRESS, \
+ (A_TARGET_READ(scn, (CE_ctrl_addr) + \
+ CE_CTRL1_ADDRESS) & ~CE_CTRL1_DMAX_LENGTH_MASK) | \
+ CE_CTRL1_DMAX_LENGTH_SET(n))
+
+#define CE_CMD_REGISTER_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + CE_CMD_REGISTER)
+
+#define CE_CMD_REGISTER_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_CMD_REGISTER, n)
+
+#define CE_MSI_ADDR_LOW_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_MSI_ADDRESS, (addr))
+
+#define CE_MSI_ADDR_HIGH_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_MSI_ADDRESS_HIGH, (addr))
+
+#define CE_MSI_DATA_SET(scn, CE_ctrl_addr, data) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_MSI_DATA, (data))
+
+#define CE_CTRL_REGISTER1_SET(scn, CE_ctrl_addr, val) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_CTRL1_ADDRESS, val)
+
+#define CE_CTRL_REGISTER1_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + CE_CTRL1_ADDRESS)
+
+#define CE_SRC_RING_BYTE_SWAP_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + CE_CTRL1_ADDRESS, \
+ (A_TARGET_READ((targid), \
+ (CE_ctrl_addr) + CE_CTRL1_ADDRESS) \
+ & ~CE_CTRL1_SRC_RING_BYTE_SWAP_EN_MASK) | \
+ CE_CTRL1_SRC_RING_BYTE_SWAP_EN_SET(n))
+
+#define CE_DEST_RING_BYTE_SWAP_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr)+CE_CTRL1_ADDRESS, \
+ (A_TARGET_READ((targid), \
+ (CE_ctrl_addr) + CE_CTRL1_ADDRESS) \
+ & ~CE_CTRL1_DST_RING_BYTE_SWAP_EN_MASK) | \
+ CE_CTRL1_DST_RING_BYTE_SWAP_EN_SET(n))
+
+#define CE_DEST_RING_READ_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + CURRENT_DRRI_ADDRESS)
+
+#define CE_DEST_RING_BASE_ADDR_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DR_BA_ADDRESS, (addr))
+
+#define CE_DEST_RING_BASE_ADDR_HIGH_SET(scn, CE_ctrl_addr, addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DR_BA_ADDRESS_HIGH, (addr))
+
+#define CE_DEST_RING_BASE_ADDR_HIGH_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + DR_BA_ADDRESS_HIGH)
+
+#define CE_DEST_RING_SZ_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DR_SIZE_ADDRESS, (n))
+
+#define CE_SRC_RING_HIGHMARK_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SRC_WATERMARK_ADDRESS, \
+ (A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + SRC_WATERMARK_ADDRESS) \
+ & ~SRC_WATERMARK_HIGH_MASK) | \
+ SRC_WATERMARK_HIGH_SET(n))
+
+#define CE_SRC_RING_LOWMARK_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SRC_WATERMARK_ADDRESS, \
+ (A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + SRC_WATERMARK_ADDRESS) \
+ & ~SRC_WATERMARK_LOW_MASK) | \
+ SRC_WATERMARK_LOW_SET(n))
+
+#define CE_DEST_RING_HIGHMARK_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DST_WATERMARK_ADDRESS, \
+ (A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + DST_WATERMARK_ADDRESS) \
+ & ~DST_WATERMARK_HIGH_MASK) | \
+ DST_WATERMARK_HIGH_SET(n))
+
+#define CE_DEST_RING_LOWMARK_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DST_WATERMARK_ADDRESS, \
+ (A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + DST_WATERMARK_ADDRESS) \
+ & ~DST_WATERMARK_LOW_MASK) | \
+ DST_WATERMARK_LOW_SET(n))
+
+#define CE_COPY_COMPLETE_INTR_ENABLE(scn, CE_ctrl_addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + HOST_IE_ADDRESS, \
+ A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + HOST_IE_ADDRESS) | \
+ HOST_IE_COPY_COMPLETE_MASK)
+
+#define CE_COPY_COMPLETE_INTR_DISABLE(scn, CE_ctrl_addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + HOST_IE_ADDRESS, \
+ A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + HOST_IE_ADDRESS) \
+ & ~HOST_IE_COPY_COMPLETE_MASK)
+
+#define CE_BASE_ADDRESS(CE_id) \
+ CE0_BASE_ADDRESS + ((CE1_BASE_ADDRESS - \
+ CE0_BASE_ADDRESS)*(CE_id))
+
+#define CE_WATERMARK_INTR_ENABLE(scn, CE_ctrl_addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + HOST_IE_ADDRESS, \
+ A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + HOST_IE_ADDRESS) | \
+ CE_WATERMARK_MASK)
+
+#define CE_WATERMARK_INTR_DISABLE(scn, CE_ctrl_addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + HOST_IE_ADDRESS, \
+ A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + HOST_IE_ADDRESS) \
+ & ~CE_WATERMARK_MASK)
+
+#define CE_ERROR_INTR_ENABLE(scn, CE_ctrl_addr) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + MISC_IE_ADDRESS, \
+ A_TARGET_READ(scn, \
+ (CE_ctrl_addr) + MISC_IE_ADDRESS) | CE_ERROR_MASK)
+
+#define CE_MISC_INT_STATUS_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + MISC_IS_ADDRESS)
+
+#define CE_ENGINE_INT_STATUS_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + HOST_IS_ADDRESS)
+
+#define CE_ENGINE_INT_STATUS_CLEAR(scn, CE_ctrl_addr, mask) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + HOST_IS_ADDRESS, (mask))
+
+#define CE_WRAPPER_INDEX_BASE_LOW_SET(scn, n) \
+ A_TARGET_WRITE(scn, \
+ CE_WRAPPER_INDEX_BASE_LOW + CE_WRAPPER_BASE_ADDRESS, n)
+
+#define CE_WRAPPER_INDEX_BASE_HIGH_SET(scn, n) \
+ A_TARGET_WRITE(scn, \
+ CE_WRAPPER_INDEX_BASE_HIGH + CE_WRAPPER_BASE_ADDRESS, n)
+
+#define CE_WATERMARK_MASK (HOST_IS_SRC_RING_LOW_WATERMARK_MASK | \
+ HOST_IS_SRC_RING_HIGH_WATERMARK_MASK | \
+ HOST_IS_DST_RING_LOW_WATERMARK_MASK | \
+ HOST_IS_DST_RING_HIGH_WATERMARK_MASK)
+
+#define CE_ERROR_MASK (MISC_IS_AXI_ERR_MASK | \
+ MISC_IS_DST_ADDR_ERR_MASK | \
+ MISC_IS_SRC_LEN_ERR_MASK | \
+ MISC_IS_DST_MAX_LEN_VIO_MASK | \
+ MISC_IS_DST_RING_OVERFLOW_MASK | \
+ MISC_IS_SRC_RING_OVERFLOW_MASK)
+
+#define CE_SRC_RING_TO_DESC(baddr, idx) \
+ (&(((struct CE_src_desc *)baddr)[idx]))
+#define CE_DEST_RING_TO_DESC(baddr, idx) \
+ (&(((struct CE_dest_desc *)baddr)[idx]))
+
+/* Ring arithmetic (modulus number of entries in ring, which is a pwr of 2). */
+#define CE_RING_DELTA(nentries_mask, fromidx, toidx) \
+ (((int)(toidx)-(int)(fromidx)) & (nentries_mask))
+
+#define CE_RING_IDX_INCR(nentries_mask, idx) \
+ (((idx) + 1) & (nentries_mask))
+
+#define CE_RING_IDX_ADD(nentries_mask, idx, num) \
+ (((idx) + (num)) & (nentries_mask))
+
+#define CE_INTERRUPT_SUMMARY(scn) \
+ CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \
+ A_TARGET_READ(scn, CE_WRAPPER_BASE_ADDRESS + \
+ CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS))
+
+/*Macro to increment CE packet errors*/
+#define OL_ATH_CE_PKT_ERROR_COUNT_INCR(_scn, _ce_ecode) \
+ do { if (_ce_ecode == CE_RING_DELTA_FAIL) \
+ (_scn->pkt_stats.ce_ring_delta_fail_count) \
+ += 1; } while (0)
+
+/* Given a Copy Engine's ID, determine the interrupt number for that
+ * copy engine's interrupts.
+ */
+#define CE_ID_TO_INUM(id) (A_INUM_CE0_COPY_COMP_BASE + (id))
+#define CE_INUM_TO_ID(inum) ((inum) - A_INUM_CE0_COPY_COMP_BASE)
+#define CE0_BASE_ADDRESS (scn->target_ce_def->d_CE0_BASE_ADDRESS)
+#define CE1_BASE_ADDRESS (scn->target_ce_def->d_CE1_BASE_ADDRESS)
+
+#ifdef ADRASTEA_SHADOW_REGISTERS
+
+#define NUM_SHADOW_REGISTERS 24
+
+#define COPY_ENGINE_ID(COPY_ENGINE_BASE_ADDRESS) ((COPY_ENGINE_BASE_ADDRESS \
+ - CE0_BASE_ADDRESS)/(CE1_BASE_ADDRESS - CE0_BASE_ADDRESS))
+
+u32 shadow_sr_wr_ind_addr(struct ol_softc *scn, u32 ctrl_addr);
+u32 shadow_dst_wr_ind_addr(struct ol_softc *scn, u32 ctrl_addr);
+#define CE_SRC_RING_WRITE_IDX_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, shadow_sr_wr_ind_addr(scn, CE_ctrl_addr), n)
+
+#define CE_SRC_RING_WRITE_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, shadow_sr_wr_ind_addr(scn, CE_ctrl_addr))
+
+#define CE_DEST_RING_WRITE_IDX_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, shadow_dst_wr_ind_addr(scn, CE_ctrl_addr), n)
+
+#define CE_DEST_RING_WRITE_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, shadow_dst_wr_ind_addr(scn, CE_ctrl_addr))
+
+#else
+
+#define CE_SRC_RING_WRITE_IDX_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + SR_WR_INDEX_ADDRESS, (n))
+
+#define CE_SRC_RING_WRITE_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + SR_WR_INDEX_ADDRESS)
+
+#define CE_DEST_RING_WRITE_IDX_SET(scn, CE_ctrl_addr, n) \
+ A_TARGET_WRITE(scn, (CE_ctrl_addr) + DST_WR_INDEX_ADDRESS, (n))
+
+#define CE_DEST_RING_WRITE_IDX_GET(scn, CE_ctrl_addr) \
+ A_TARGET_READ(scn, (CE_ctrl_addr) + DST_WR_INDEX_ADDRESS)
+
+#endif
+
+#endif /* __CE_REG_H__ */
diff --git a/core/hif/src/ce/ce_service.c b/core/hif/src/ce/ce_service.c
new file mode 100644
index 0000000..d27ca6b
--- /dev/null
+++ b/core/hif/src/ce/ce_service.c
@@ -0,0 +1,1678 @@
+/*
+ * Copyright (c) 2013-2015 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.h"
+#include "hif_io32.h"
+#include "ce_api.h"
+#include "ce_main.h"
+#include "ce_internal.h"
+#include "ce_reg.h"
+#include "cdf_lock.h"
+#include "regtable.h"
+#include <cds_get_bin.h>
+#include "epping_main.h"
+#include "hif_main.h"
+#include "hif_debug.h"
+
+#ifdef IPA_OFFLOAD
+#ifdef QCA_WIFI_3_0
+#define CE_IPA_RING_INIT(ce_desc) \
+ do { \
+ ce_desc->gather = 0; \
+ ce_desc->enable_11h = 0; \
+ ce_desc->meta_data_low = 0; \
+ ce_desc->packet_result_offset = 64; \
+ ce_desc->toeplitz_hash_enable = 0; \
+ ce_desc->addr_y_search_disable = 0; \
+ ce_desc->addr_x_search_disable = 0; \
+ ce_desc->misc_int_disable = 0; \
+ ce_desc->target_int_disable = 0; \
+ ce_desc->host_int_disable = 0; \
+ ce_desc->dest_byte_swap = 0; \
+ ce_desc->byte_swap = 0; \
+ ce_desc->type = 2; \
+ ce_desc->tx_classify = 1; \
+ ce_desc->buffer_addr_hi = 0; \
+ ce_desc->meta_data = 0; \
+ ce_desc->nbytes = 128; \
+ } while (0)
+#else
+#define CE_IPA_RING_INIT(ce_desc) \
+ do { \
+ ce_desc->byte_swap = 0; \
+ ce_desc->nbytes = 60; \
+ ce_desc->gather = 0; \
+ } while (0)
+#endif /* QCA_WIFI_3_0 */
+#endif /* IPA_OFFLOAD */
+
+static int war1_allow_sleep;
+/* io32 write workaround */
+static int hif_ce_war1;
+
+/*
+ * Support for Copy Engine hardware, which is mainly used for
+ * communication between Host and Target over a PCIe interconnect.
+ */
+
+/*
+ * 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.
+ * Target TX harsh result toeplitz_hash_result
+ */
+
+/*
+ * 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,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *sw_idx, unsigned int *hw_idx,
+ uint32_t *toeplitz_hash_result);
+
+void war_ce_src_ring_write_idx_set(struct ol_softc *scn,
+ u32 ctrl_addr, unsigned int write_index)
+{
+ if (hif_ce_war1) {
+ void __iomem *indicator_addr;
+
+ indicator_addr = scn->mem + ctrl_addr + DST_WATERMARK_ADDRESS;
+
+ if (!war1_allow_sleep
+ && ctrl_addr == CE_BASE_ADDRESS(CDC_WAR_DATA_CE)) {
+ hif_write32_mb(indicator_addr,
+ (CDC_WAR_MAGIC_STR | write_index));
+ } else {
+ unsigned long irq_flags;
+ local_irq_save(irq_flags);
+ hif_write32_mb(indicator_addr, 1);
+
+ /*
+ * PCIE write waits for ACK in IPQ8K, there is no
+ * need to read back value.
+ */
+ (void)hif_read32_mb(indicator_addr);
+ (void)hif_read32_mb(indicator_addr); /* conservative */
+
+ CE_SRC_RING_WRITE_IDX_SET(scn,
+ ctrl_addr, write_index);
+
+ hif_write32_mb(indicator_addr, 0);
+ local_irq_restore(irq_flags);
+ }
+ } else
+ CE_SRC_RING_WRITE_IDX_SET(scn, ctrl_addr, write_index);
+}
+
+int
+ce_send_nolock(struct CE_handle *copyeng,
+ void *per_transfer_context,
+ cdf_dma_addr_t buffer,
+ uint32_t nbytes,
+ uint32_t transfer_id,
+ uint32_t flags,
+ uint32_t user_flags)
+{
+ int status;
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ struct CE_ring_state *src_ring = CE_state->src_ring;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int sw_index = src_ring->sw_index;
+ unsigned int write_index = src_ring->write_index;
+ uint64_t dma_addr = buffer;
+ struct ol_softc *scn = CE_state->scn;
+
+ A_TARGET_ACCESS_BEGIN_RET(scn);
+ if (unlikely(CE_RING_DELTA(nentries_mask,
+ write_index, sw_index - 1) <= 0)) {
+ OL_ATH_CE_PKT_ERROR_COUNT_INCR(scn, CE_RING_DELTA_FAIL);
+ status = CDF_STATUS_E_FAILURE;
+ A_TARGET_ACCESS_END_RET(scn);
+ return status;
+ }
+ {
+ 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 low 32 bits source descriptor address */
+ shadow_src_desc->buffer_addr =
+ (uint32_t)(dma_addr & 0xFFFFFFFF);
+#ifdef QCA_WIFI_3_0
+ shadow_src_desc->buffer_addr_hi =
+ (uint32_t)((dma_addr >> 32) & 0x1F);
+ user_flags |= shadow_src_desc->buffer_addr_hi;
+ memcpy(&(((uint32_t *)shadow_src_desc)[1]), &user_flags,
+ sizeof(uint32_t));
+#endif
+ 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) {
+ war_ce_src_ring_write_idx_set(scn, ctrl_addr,
+ write_index);
+ }
+
+ src_ring->write_index = write_index;
+ status = CDF_STATUS_SUCCESS;
+ }
+ A_TARGET_ACCESS_END_RET(scn);
+
+ return status;
+}
+
+int
+ce_send(struct CE_handle *copyeng,
+ void *per_transfer_context,
+ cdf_dma_addr_t buffer,
+ uint32_t nbytes,
+ uint32_t transfer_id,
+ uint32_t flags,
+ uint32_t user_flag)
+{
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ int status;
+
+ cdf_spin_lock_bh(&CE_state->scn->target_lock);
+ status = ce_send_nolock(copyeng, per_transfer_context, buffer, nbytes,
+ transfer_id, flags, user_flag);
+ cdf_spin_unlock_bh(&CE_state->scn->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,
+ cdf_dma_addr_t buffer,
+ uint32_t nbytes,
+ uint32_t flags,
+ uint32_t user_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) {
+ CDF_ASSERT(num_items < CE_SENDLIST_ITEMS_MAX);
+ return CDF_STATUS_E_RESOURCES;
+ }
+
+ item = &sl->item[num_items];
+ item->send_type = CE_SIMPLE_BUFFER_TYPE;
+ item->data = buffer;
+ item->u.nbytes = nbytes;
+ item->flags = flags;
+ item->user_flags = user_flags;
+ sl->num_items = num_items + 1;
+ return CDF_STATUS_SUCCESS;
+}
+
+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;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int num_items = sl->num_items;
+ unsigned int sw_index;
+ unsigned int write_index;
+
+ CDF_ASSERT((num_items > 0) && (num_items < src_ring->nentries));
+
+ cdf_spin_lock_bh(&CE_state->scn->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? */
+ CDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE);
+ status = ce_send_nolock(copyeng, CE_SENDLIST_ITEM_CTXT,
+ (cdf_dma_addr_t) item->data,
+ item->u.nbytes, transfer_id,
+ item->flags | CE_SEND_FLAG_GATHER,
+ item->user_flags);
+ CDF_ASSERT(status == CDF_STATUS_SUCCESS);
+ }
+ /* provide valid context pointer for final item */
+ item = &sl->item[i];
+ /* TBDXXX: Support extensible sendlist_types? */
+ CDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE);
+ status = ce_send_nolock(copyeng, per_transfer_context,
+ (cdf_dma_addr_t) item->data,
+ item->u.nbytes,
+ transfer_id, item->flags,
+ item->user_flags);
+ CDF_ASSERT(status == CDF_STATUS_SUCCESS);
+ NBUF_UPDATE_TX_PKT_COUNT((cdf_nbuf_t)per_transfer_context,
+ NBUF_TX_PKT_CE);
+ DPTRACE(cdf_dp_trace((cdf_nbuf_t)per_transfer_context,
+ CDF_DP_TRACE_CE_PACKET_PTR_RECORD,
+ (uint8_t *)(((cdf_nbuf_t)per_transfer_context)->data),
+ sizeof(((cdf_nbuf_t)per_transfer_context)->data)));
+ } 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.
+ */
+ }
+ cdf_spin_unlock_bh(&CE_state->scn->target_lock);
+
+ return status;
+}
+
+#ifdef WLAN_FEATURE_FASTPATH
+#ifdef QCA_WIFI_3_0
+static inline void
+ce_buffer_addr_hi_set(struct CE_src_desc *shadow_src_desc,
+ uint64_t dma_addr,
+ uint32_t user_flags)
+{
+ shadow_src_desc->buffer_addr_hi =
+ (uint32_t)((dma_addr >> 32) & 0x1F);
+ user_flags |= shadow_src_desc->buffer_addr_hi;
+ memcpy(&(((uint32_t *)shadow_src_desc)[1]), &user_flags,
+ sizeof(uint32_t));
+}
+#else
+static inline void
+ce_buffer_addr_hi_set(struct CE_src_desc *shadow_src_desc,
+ uint64_t dma_addr,
+ uint32_t user_flags)
+{
+}
+#endif
+
+/**
+ * ce_send_fast() CE layer Tx buffer posting function
+ * @copyeng: copy engine handle
+ * @msdus: iarray of msdu to be sent
+ * @num_msdus: number of msdus in an array
+ * @transfer_id: transfer_id
+ *
+ * Assumption : Called with an array of MSDU's
+ * Function:
+ * For each msdu in the array
+ * 1. Check no. of available entries
+ * 2. Create src ring entries (allocated in consistent memory
+ * 3. Write index to h/w
+ *
+ * Return: No. of packets that could be sent
+ */
+
+int ce_send_fast(struct CE_handle *copyeng, cdf_nbuf_t *msdus,
+ unsigned int num_msdus, unsigned int transfer_id)
+{
+ struct CE_state *ce_state = (struct CE_state *)copyeng;
+ struct ol_softc *scn = ce_state->scn;
+ struct CE_ring_state *src_ring = ce_state->src_ring;
+ u_int32_t ctrl_addr = ce_state->ctrl_addr;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int write_index;
+ unsigned int sw_index;
+ unsigned int frag_len;
+ cdf_nbuf_t msdu;
+ int i;
+ uint64_t dma_addr;
+ uint32_t user_flags = 0;
+
+ /*
+ * This lock could be more fine-grained, one per CE,
+ * TODO : Add this lock now.
+ * That is the next step of optimization.
+ */
+ cdf_spin_lock_bh(&scn->target_lock);
+ sw_index = src_ring->sw_index;
+ write_index = src_ring->write_index;
+
+ /* 2 msdus per packet */
+ for (i = 0; i < num_msdus; i++) {
+ 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);
+
+ msdu = msdus[i];
+
+ /*
+ * First fill out the ring descriptor for the HTC HTT frame
+ * header. These are uncached writes. Should we use a local
+ * structure instead?
+ */
+ /* HTT/HTC header can be passed as a argument */
+ dma_addr = cdf_nbuf_get_frag_paddr_lo(msdu, 0);
+ shadow_src_desc->buffer_addr = (uint32_t)(dma_addr &
+ 0xFFFFFFFF);
+ user_flags = cdf_nbuf_data_attr_get(msdu) & DESC_DATA_FLAG_MASK;
+ ce_buffer_addr_hi_set(shadow_src_desc, dma_addr, user_flags);
+
+ shadow_src_desc->meta_data = transfer_id;
+ shadow_src_desc->nbytes = cdf_nbuf_get_frag_len(msdu, 0);
+
+ /*
+ * HTC HTT header is a word stream, so byte swap if CE byte
+ * swap enabled
+ */
+ shadow_src_desc->byte_swap = ((ce_state->attr_flags &
+ CE_ATTR_BYTE_SWAP_DATA) != 0);
+ /* For the first one, it still does not need to write */
+ shadow_src_desc->gather = 1;
+ *src_desc = *shadow_src_desc;
+
+ /* By default we could initialize the transfer context to this
+ * value
+ */
+ src_ring->per_transfer_context[write_index] =
+ CE_SENDLIST_ITEM_CTXT;
+
+ write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+
+ src_desc = CE_SRC_RING_TO_DESC(src_ring_base, write_index);
+ shadow_src_desc = CE_SRC_RING_TO_DESC(shadow_base, write_index);
+ /*
+ * Now fill out the ring descriptor for the actual data
+ * packet
+ */
+ dma_addr = cdf_nbuf_get_frag_paddr_lo(msdu, 1);
+ shadow_src_desc->buffer_addr = (uint32_t)(dma_addr &
+ 0xFFFFFFFF);
+ /*
+ * Clear packet offset for all but the first CE desc.
+ */
+ user_flags &= ~CDF_CE_TX_PKT_OFFSET_BIT_M;
+ ce_buffer_addr_hi_set(shadow_src_desc, dma_addr, user_flags);
+ shadow_src_desc->meta_data = transfer_id;
+
+ /* get actual packet length */
+ frag_len = cdf_nbuf_get_frag_len(msdu, 1);
+ shadow_src_desc->nbytes = frag_len > ce_state->download_len ?
+ ce_state->download_len : frag_len;
+
+ /* Data packet is a byte stream, so disable byte swap */
+ shadow_src_desc->byte_swap = 0;
+ /* For the last one, gather is not set */
+ shadow_src_desc->gather = 0;
+ *src_desc = *shadow_src_desc;
+ src_ring->per_transfer_context[write_index] = msdu;
+ write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+ }
+
+ /* Write the final index to h/w one-shot */
+ if (i) {
+ src_ring->write_index = write_index;
+ /* Don't call WAR_XXX from here
+ * Just call XXX instead, that has the reqd. intel
+ */
+ war_ce_src_ring_write_idx_set(scn, ctrl_addr, write_index);
+ }
+
+ cdf_spin_unlock_bh(&scn->target_lock);
+
+ /*
+ * If all packets in the array are transmitted,
+ * i = num_msdus
+ * Temporarily add an ASSERT
+ */
+ ASSERT(i == num_msdus);
+ return i;
+}
+#endif /* WLAN_FEATURE_FASTPATH */
+
+int
+ce_recv_buf_enqueue(struct CE_handle *copyeng,
+ void *per_recv_context, cdf_dma_addr_t buffer)
+{
+ int status;
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ struct CE_ring_state *dest_ring = CE_state->dest_ring;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ unsigned int nentries_mask = dest_ring->nentries_mask;
+ unsigned int write_index;
+ unsigned int sw_index;
+ int val = 0;
+ uint64_t dma_addr = buffer;
+ struct ol_softc *scn = CE_state->scn;
+
+ cdf_spin_lock_bh(&scn->target_lock);
+ write_index = dest_ring->write_index;
+ sw_index = dest_ring->sw_index;
+
+ A_TARGET_ACCESS_BEGIN_RET_EXT(scn, val);
+ if (val == -1) {
+ cdf_spin_unlock_bh(&scn->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 low 32 bit destination descriptor */
+ dest_desc->buffer_addr = (uint32_t)(dma_addr & 0xFFFFFFFF);
+#ifdef QCA_WIFI_3_0
+ dest_desc->buffer_addr_hi =
+ (uint32_t)((dma_addr >> 32) & 0x1F);
+#endif
+ dest_desc->nbytes = 0;
+
+ dest_ring->per_transfer_context[write_index] =
+ per_recv_context;
+
+ /* Update Destination Ring Write Index */
+ write_index = CE_RING_IDX_INCR(nentries_mask, write_index);
+ CE_DEST_RING_WRITE_IDX_SET(scn, ctrl_addr, write_index);
+ dest_ring->write_index = write_index;
+ status = CDF_STATUS_SUCCESS;
+ } else {
+ status = CDF_STATUS_E_FAILURE;
+ }
+ A_TARGET_ACCESS_END_RET_EXT(scn, val);
+ if (val == -1) {
+ cdf_spin_unlock_bh(&scn->target_lock);
+ return val;
+ }
+
+ cdf_spin_unlock_bh(&scn->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;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ struct ol_softc *scn = CE_state->scn;
+
+ cdf_spin_lock(&scn->target_lock);
+ CE_SRC_RING_LOWMARK_SET(scn, ctrl_addr, low_alert_nentries);
+ CE_SRC_RING_HIGHMARK_SET(scn, ctrl_addr, high_alert_nentries);
+ cdf_spin_unlock(&scn->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;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ struct ol_softc *scn = CE_state->scn;
+
+ cdf_spin_lock(&scn->target_lock);
+ CE_DEST_RING_LOWMARK_SET(scn, ctrl_addr,
+ low_alert_nentries);
+ CE_DEST_RING_HIGHMARK_SET(scn, ctrl_addr,
+ high_alert_nentries);
+ cdf_spin_unlock(&scn->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;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int sw_index;
+ unsigned int write_index;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ sw_index = src_ring->sw_index;
+ write_index = src_ring->write_index;
+ cdf_spin_unlock(&CE_state->scn->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;
+ unsigned int nentries_mask = dest_ring->nentries_mask;
+ unsigned int sw_index;
+ unsigned int write_index;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ sw_index = dest_ring->sw_index;
+ write_index = dest_ring->write_index;
+ cdf_spin_unlock(&CE_state->scn->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 ol_softc *scn,
+ struct CE_state *CE_state)
+{
+ struct CE_ring_state *src_ring = CE_state->src_ring;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ 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(scn, 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;
+ unsigned int nentries;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ nentries = ce_send_entries_done_nolock(CE_state->scn, CE_state);
+ cdf_spin_unlock(&CE_state->scn->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 ol_softc *scn,
+ struct CE_state *CE_state)
+{
+ struct CE_ring_state *dest_ring = CE_state->dest_ring;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ 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(scn, 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;
+ unsigned int nentries;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ nentries = ce_recv_entries_done_nolock(CE_state->scn, CE_state);
+ cdf_spin_unlock(&CE_state->scn->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,
+ cdf_dma_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 CE_dest_desc 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;
+ 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 = CDF_STATUS_E_FAILURE;
+ goto done;
+ }
+
+ dest_desc->nbytes = 0;
+
+ /* Return data from completed destination descriptor */
+ *bufferp = HIF_CE_DESC_ADDR_TO_DMA(&dest_desc_info);
+ *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 = CDF_STATUS_SUCCESS;
+
+done:
+ return status;
+}
+
+int
+ce_completed_recv_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp, unsigned int *flagsp)
+{
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ int status;
+
+ cdf_spin_lock_bh(&CE_state->scn->target_lock);
+ status =
+ ce_completed_recv_next_nolock(CE_state, per_CE_contextp,
+ per_transfer_contextp, bufferp,
+ nbytesp, transfer_idp, flagsp);
+ cdf_spin_unlock_bh(&CE_state->scn->target_lock);
+
+ return status;
+}
+
+/* NB: Modeled after ce_completed_recv_next_nolock */
+CDF_STATUS
+ce_revoke_recv_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp, cdf_dma_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;
+ CDF_STATUS status;
+ struct ol_softc *scn;
+
+ CE_state = (struct CE_state *)copyeng;
+ dest_ring = CE_state->dest_ring;
+ if (!dest_ring) {
+ return CDF_STATUS_E_FAILURE;
+ }
+
+ scn = CE_state->scn;
+ cdf_spin_lock(&scn->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 = HIF_CE_DESC_ADDR_TO_DMA(dest_desc);
+
+ 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 = CDF_STATUS_SUCCESS;
+ } else {
+ status = CDF_STATUS_E_FAILURE;
+ }
+ cdf_spin_unlock(&scn->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,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *sw_idx,
+ unsigned int *hw_idx,
+ uint32_t *toeplitz_hash_result)
+{
+ int status = CDF_STATUS_E_FAILURE;
+ struct CE_ring_state *src_ring = CE_state->src_ring;
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ unsigned int nentries_mask = src_ring->nentries_mask;
+ unsigned int sw_index = src_ring->sw_index;
+ unsigned int read_index;
+ struct ol_softc *scn = CE_state->scn;
+
+ 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(scn);
+ src_ring->hw_index =
+ CE_SRC_RING_READ_IDX_GET(scn, ctrl_addr);
+ A_TARGET_ACCESS_END_RET(scn);
+ }
+ 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);
+#ifdef QCA_WIFI_3_0
+ 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);
+#endif
+ /* Return data from completed source descriptor */
+ *bufferp = HIF_CE_DESC_ADDR_TO_DMA(shadow_src_desc);
+ *nbytesp = shadow_src_desc->nbytes;
+ *transfer_idp = shadow_src_desc->meta_data;
+#ifdef QCA_WIFI_3_0
+ *toeplitz_hash_result = src_desc->toeplitz_hash_result;
+#else
+ *toeplitz_hash_result = 0;
+#endif
+ 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 = CDF_STATUS_SUCCESS;
+ }
+
+ return status;
+}
+
+/* NB: Modeled after ce_completed_send_next */
+CDF_STATUS
+ce_cancel_send_next(struct CE_handle *copyeng,
+ void **per_CE_contextp,
+ void **per_transfer_contextp,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ uint32_t *toeplitz_hash_result)
+{
+ struct CE_state *CE_state;
+ struct CE_ring_state *src_ring;
+ unsigned int nentries_mask;
+ unsigned int sw_index;
+ unsigned int write_index;
+ CDF_STATUS status;
+ struct ol_softc *scn;
+
+ CE_state = (struct CE_state *)copyeng;
+ src_ring = CE_state->src_ring;
+ if (!src_ring) {
+ return CDF_STATUS_E_FAILURE;
+ }
+
+ scn = CE_state->scn;
+ cdf_spin_lock(&CE_state->scn->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 = HIF_CE_DESC_ADDR_TO_DMA(src_desc);
+ *nbytesp = src_desc->nbytes;
+ *transfer_idp = src_desc->meta_data;
+#ifdef QCA_WIFI_3_0
+ *toeplitz_hash_result = src_desc->toeplitz_hash_result;
+#else
+ *toeplitz_hash_result = 0;
+#endif
+
+ 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 = CDF_STATUS_SUCCESS;
+ } else {
+ status = CDF_STATUS_E_FAILURE;
+ }
+ cdf_spin_unlock(&CE_state->scn->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,
+ cdf_dma_addr_t *bufferp,
+ unsigned int *nbytesp,
+ unsigned int *transfer_idp,
+ unsigned int *sw_idx,
+ unsigned int *hw_idx,
+ unsigned int *toeplitz_hash_result)
+{
+ struct CE_state *CE_state = (struct CE_state *)copyeng;
+ int status;
+
+ cdf_spin_lock_bh(&CE_state->scn->target_lock);
+ status =
+ ce_completed_send_next_nolock(CE_state, per_CE_contextp,
+ per_transfer_contextp, bufferp,
+ nbytesp, transfer_idp, sw_idx,
+ hw_idx, toeplitz_hash_result);
+ cdf_spin_unlock_bh(&CE_state->scn->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
+ * hif_send_complete_check so should not countain recieve functionality
+ * within it .
+ */
+
+void ce_per_engine_servicereap(struct ol_softc *scn, unsigned int CE_id)
+{
+ void *CE_context;
+ void *transfer_context;
+ cdf_dma_addr_t buf;
+ unsigned int nbytes;
+ unsigned int id;
+ unsigned int sw_idx, hw_idx;
+ uint32_t toeplitz_hash_result;
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+
+ A_TARGET_ACCESS_BEGIN(scn);
+
+ /* 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 occured
+ * 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.
+ */
+
+ cdf_spin_lock_bh(&scn->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,
+ &toeplitz_hash_result) ==
+ CDF_STATUS_SUCCESS) {
+ if (CE_id != CE_HTT_H2T_MSG) {
+ cdf_spin_unlock_bh(&scn->target_lock);
+ CE_state->
+ send_cb((struct CE_handle *)
+ CE_state, CE_context,
+ transfer_context, buf,
+ nbytes, id, sw_idx, hw_idx,
+ toeplitz_hash_result);
+ cdf_spin_lock_bh(&scn->target_lock);
+ } else {
+ struct HIF_CE_pipe_info *pipe_info =
+ (struct HIF_CE_pipe_info *)
+ CE_context;
+
+ cdf_spin_lock_bh(&pipe_info->
+ completion_freeq_lock);
+ pipe_info->num_sends_allowed++;
+ cdf_spin_unlock_bh(&pipe_info->
+ completion_freeq_lock);
+ }
+ }
+ }
+ }
+
+ cdf_spin_unlock_bh(&scn->target_lock);
+ A_TARGET_ACCESS_END(scn);
+}
+
+#endif /*ATH_11AC_TXCOMPACT */
+
+/*
+ * 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.
+ *
+ * Returns: number of messages processed
+ */
+
+int ce_per_engine_service(struct ol_softc *scn, unsigned int CE_id)
+{
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ void *CE_context;
+ void *transfer_context;
+ cdf_dma_addr_t buf;
+ unsigned int nbytes;
+ unsigned int id;
+ unsigned int flags;
+ uint32_t CE_int_status;
+ unsigned int more_comp_cnt = 0;
+ unsigned int more_snd_comp_cnt = 0;
+ unsigned int sw_idx, hw_idx;
+ uint32_t toeplitz_hash_result;
+
+ if (Q_TARGET_ACCESS_BEGIN(scn) < 0) {
+ HIF_ERROR("[premature rc=0]\n");
+ return 0; /* no work done */
+ }
+
+ cdf_spin_lock(&scn->target_lock);
+
+ /* Clear force_break flag and re-initialize receive_count to 0 */
+
+ /* NAPI: scn variables- thread/multi-processing safety? */
+ scn->receive_count = 0;
+ scn->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) ==
+ CDF_STATUS_SUCCESS) {
+ cdf_spin_unlock(&scn->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 (cdf_unlikely(scn->force_break)) {
+ cdf_atomic_set(&CE_state->rx_pending, 1);
+ CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr,
+ HOST_IS_COPY_COMPLETE_MASK);
+ if (Q_TARGET_ACCESS_END(scn) < 0)
+ HIF_ERROR("<--[premature rc=%d]\n",
+ scn->receive_count);
+ return scn->receive_count;
+ }
+ cdf_spin_lock(&scn->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,
+ &toeplitz_hash_result) == CDF_STATUS_SUCCESS) {
+
+ if (CE_id != CE_HTT_H2T_MSG ||
+ WLAN_IS_EPPING_ENABLED(cds_get_conparam())) {
+ cdf_spin_unlock(&scn->target_lock);
+ CE_state->send_cb((struct CE_handle *)CE_state,
+ CE_context, transfer_context,
+ buf, nbytes, id, sw_idx,
+ hw_idx, toeplitz_hash_result);
+ cdf_spin_lock(&scn->target_lock);
+ } else {
+ struct HIF_CE_pipe_info *pipe_info =
+ (struct HIF_CE_pipe_info *)CE_context;
+
+ cdf_spin_lock(&pipe_info->
+ completion_freeq_lock);
+ pipe_info->num_sends_allowed++;
+ cdf_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,
+ &toeplitz_hash_result) == CDF_STATUS_SUCCESS) {
+ cdf_spin_unlock(&scn->target_lock);
+ CE_state->send_cb((struct CE_handle *)CE_state,
+ CE_context, transfer_context, buf,
+ nbytes, id, sw_idx, hw_idx,
+ toeplitz_hash_result);
+ cdf_spin_lock(&scn->target_lock);
+ }
+#endif /*ATH_11AC_TXCOMPACT */
+ }
+
+more_watermarks:
+ if (CE_state->misc_cbs) {
+ CE_int_status = CE_ENGINE_INT_STATUS_GET(scn, ctrl_addr);
+ if (CE_int_status & CE_WATERMARK_MASK) {
+ if (CE_state->watermark_cb) {
+
+ cdf_spin_unlock(&scn->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);
+ cdf_spin_lock(&scn->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(scn, 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 && ce_recv_entries_done_nolock(scn, CE_state)) {
+ if (WLAN_IS_EPPING_ENABLED(cds_get_conparam()) ||
+ more_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) {
+ goto more_completions;
+ } else {
+ HIF_ERROR(
+ "%s:Potential infinite loop detected during Rx processing nentries_mask:0x%x sw read_idx:0x%x hw read_idx:0x%x",
+ __func__, CE_state->dest_ring->nentries_mask,
+ CE_state->dest_ring->sw_index,
+ CE_DEST_RING_READ_IDX_GET(scn,
+ CE_state->ctrl_addr));
+ }
+ }
+
+ if (CE_state->send_cb && ce_send_entries_done_nolock(scn, CE_state)) {
+ if (WLAN_IS_EPPING_ENABLED(cds_get_conparam()) ||
+ more_snd_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) {
+ goto more_completions;
+ } else {
+ HIF_ERROR(
+ "%s:Potential infinite loop detected during send completion nentries_mask:0x%x sw read_idx:0x%x hw read_idx:0x%x",
+ __func__, CE_state->src_ring->nentries_mask,
+ CE_state->src_ring->sw_index,
+ CE_SRC_RING_READ_IDX_GET(scn,
+ CE_state->ctrl_addr));
+ }
+ }
+
+ if (CE_state->misc_cbs) {
+ CE_int_status = CE_ENGINE_INT_STATUS_GET(scn, ctrl_addr);
+ if (CE_int_status & CE_WATERMARK_MASK) {
+ if (CE_state->watermark_cb) {
+ goto more_watermarks;
+ }
+ }
+ }
+
+ cdf_spin_unlock(&scn->target_lock);
+ cdf_atomic_set(&CE_state->rx_pending, 0);
+
+ if (Q_TARGET_ACCESS_END(scn) < 0)
+ HIF_ERROR("<--[premature rc=%d]\n", scn->receive_count);
+ return scn->receive_count;
+}
+
+/*
+ * 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, struct ol_softc *scn)
+{
+ int CE_id;
+ uint32_t intr_summary;
+
+ A_TARGET_ACCESS_BEGIN(scn);
+ if (!cdf_atomic_read(&scn->tasklet_from_intr)) {
+ for (CE_id = 0; CE_id < scn->ce_count; CE_id++) {
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+ if (cdf_atomic_read(&CE_state->rx_pending)) {
+ cdf_atomic_set(&CE_state->rx_pending, 0);
+ ce_per_engine_service(scn, CE_id);
+ }
+ }
+
+ A_TARGET_ACCESS_END(scn);
+ return;
+ }
+
+ intr_summary = CE_INTERRUPT_SUMMARY(scn);
+
+ for (CE_id = 0; intr_summary && (CE_id < scn->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(scn, CE_id);
+ }
+
+ A_TARGET_ACCESS_END(scn);
+}
+
+/*
+ * 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)
+{
+ uint32_t ctrl_addr = CE_state->ctrl_addr;
+ struct ol_softc *scn = CE_state->scn;
+
+ CE_state->disable_copy_compl_intr = disable_copy_compl_intr;
+ A_TARGET_ACCESS_BEGIN(scn);
+ if ((!disable_copy_compl_intr) &&
+ (CE_state->send_cb || CE_state->recv_cb)) {
+ CE_COPY_COMPLETE_INTR_ENABLE(scn, ctrl_addr);
+ } else {
+ CE_COPY_COMPLETE_INTR_DISABLE(scn, ctrl_addr);
+ }
+
+ if (CE_state->watermark_cb) {
+ CE_WATERMARK_INTR_ENABLE(scn, ctrl_addr);
+ } else {
+ CE_WATERMARK_INTR_DISABLE(scn, ctrl_addr);
+ }
+ A_TARGET_ACCESS_END(scn);
+
+}
+
+/*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 ol_softc *scn)
+{
+ int CE_id;
+
+ A_TARGET_ACCESS_BEGIN(scn);
+ for (CE_id = 0; CE_id < scn->ce_count; CE_id++) {
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+ uint32_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(scn, ctrl_addr);
+ }
+
+ if (CE_state->watermark_cb) {
+ CE_WATERMARK_INTR_DISABLE(scn, ctrl_addr);
+ }
+ }
+ A_TARGET_ACCESS_END(scn);
+}
+
+void ce_enable_any_copy_compl_intr_nolock(struct ol_softc *scn)
+{
+ int CE_id;
+
+ A_TARGET_ACCESS_BEGIN(scn);
+ for (CE_id = 0; CE_id < scn->ce_count; CE_id++) {
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+ uint32_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(scn, ctrl_addr);
+ }
+
+ if (CE_state->watermark_cb) {
+ CE_WATERMARK_INTR_ENABLE(scn, ctrl_addr);
+ }
+ }
+ A_TARGET_ACCESS_END(scn);
+}
+
+void ce_disable_any_copy_compl_intr(struct ol_softc *scn)
+{
+ cdf_spin_lock(&scn->target_lock);
+ ce_disable_any_copy_compl_intr_nolock(scn);
+ cdf_spin_unlock(&scn->target_lock);
+}
+
+/*Re-enable the copy compl interrupt if it has not been disabled before.*/
+void ce_enable_any_copy_compl_intr(struct ol_softc *scn)
+{
+ cdf_spin_lock(&scn->target_lock);
+ ce_enable_any_copy_compl_intr_nolock(scn);
+ cdf_spin_unlock(&scn->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;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ CE_state->send_cb = fn_ptr;
+ CE_state->send_context = ce_send_context;
+ ce_per_engine_handler_adjust(CE_state, disable_interrupts);
+ cdf_spin_unlock(&CE_state->scn->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;
+
+ cdf_spin_lock(&CE_state->scn->target_lock);
+ CE_state->recv_cb = fn_ptr;
+ CE_state->recv_context = CE_recv_context;
+ ce_per_engine_handler_adjust(CE_state, disable_interrupts);
+ cdf_spin_unlock(&CE_state->scn->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;
+
+ cdf_spin_lock(&CE_state->scn->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;
+ }
+ cdf_spin_unlock(&CE_state->scn->target_lock);
+}
+
+#ifdef WLAN_FEATURE_FASTPATH
+/**
+ * ce_pkt_dl_len_set() set the HTT packet download length
+ * @hif_sc: HIF context
+ * @pkt_download_len: download length
+ *
+ * Return: None
+ */
+void ce_pkt_dl_len_set(void *hif_sc, u_int32_t pkt_download_len)
+{
+ struct ol_softc *sc = (struct ol_softc *)(hif_sc);
+ struct CE_state *ce_state = sc->ce_id_to_state[CE_HTT_H2T_MSG];
+
+ cdf_assert_always(ce_state);
+
+ cdf_spin_lock_bh(&sc->target_lock);
+ ce_state->download_len = pkt_download_len;
+ cdf_spin_unlock_bh(&sc->target_lock);
+
+ cdf_print("%s CE %d Pkt download length %d\n", __func__,
+ ce_state->id, ce_state->download_len);
+}
+#else
+void ce_pkt_dl_len_set(void *hif_sc, u_int32_t pkt_download_len)
+{
+}
+#endif /* WLAN_FEATURE_FASTPATH */
+
+bool ce_get_rx_pending(struct ol_softc *scn)
+{
+ int CE_id;
+
+ for (CE_id = 0; CE_id < scn->ce_count; CE_id++) {
+ struct CE_state *CE_state = scn->ce_id_to_state[CE_id];
+ if (cdf_atomic_read(&CE_state->rx_pending))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * ce_check_rx_pending() - ce_check_rx_pending
+ * @scn: ol_softc
+ * @ce_id: ce_id
+ *
+ * Return: bool
+ */
+bool ce_check_rx_pending(struct ol_softc *scn, int ce_id)
+{
+ struct CE_state *CE_state = scn->ce_id_to_state[ce_id];
+ if (cdf_atomic_read(&CE_state->rx_pending))
+ return true;
+ else
+ return false;
+}
+void ce_enable_msi(struct ol_softc *scn, unsigned int CE_id,
+ uint32_t msi_addr_lo, uint32_t msi_addr_hi,
+ uint32_t msi_data)
+{
+#ifdef WLAN_ENABLE_QCA6180
+ struct CE_state *CE_state;
+ A_target_id_t targid;
+ u_int32_t ctrl_addr;
+ uint32_t tmp;
+
+ adf_os_spin_lock(&scn->target_lock);
+ CE_state = scn->ce_id_to_state[CE_id];
+ if (!CE_state) {
+ HIF_ERROR("%s: error - CE_state = NULL", __func__);
+ adf_os_spin_unlock(&scn->target_lock);
+ return;
+ }
+ targid = TARGID(sc);
+ ctrl_addr = CE_state->ctrl_addr;
+ CE_MSI_ADDR_LOW_SET(scn, ctrl_addr, msi_addr_lo);
+ CE_MSI_ADDR_HIGH_SET(scn, ctrl_addr, msi_addr_hi);
+ CE_MSI_DATA_SET(scn, ctrl_addr, msi_data);
+ tmp = CE_CTRL_REGISTER1_GET(scn, ctrl_addr);
+ tmp |= (1 << CE_MSI_ENABLE_BIT);
+ CE_CTRL_REGISTER1_SET(scn, ctrl_addr, tmp);
+ adf_os_spin_unlock(&scn->target_lock);
+#endif
+}
+
+#ifdef IPA_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_ipa_get_resource(struct CE_handle *ce,
+ uint32_t *ce_sr_base_paddr,
+ uint32_t *ce_sr_ring_size,
+ cdf_dma_addr_t *ce_reg_paddr)
+{
+ struct CE_state *CE_state = (struct CE_state *)ce;
+ uint32_t ring_loop;
+ struct CE_src_desc *ce_desc;
+ cdf_dma_addr_t phy_mem_base;
+ struct ol_softc *scn = CE_state->scn;
+
+ 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)));
+ CE_IPA_RING_INIT(ce_desc);
+ }
+
+ /* Get BAR address */
+ hif_read_phy_mem_base(CE_state->scn, &phy_mem_base);
+
+ *ce_sr_base_paddr = (uint32_t) CE_state->src_ring->base_addr_CE_space;
+ *ce_sr_ring_size = (uint32_t) CE_state->src_ring->nentries;
+ *ce_reg_paddr = phy_mem_base + CE_BASE_ADDRESS(CE_state->id) +
+ SR_WR_INDEX_ADDRESS;
+ return;
+}
+#endif /* IPA_OFFLOAD */
+
diff --git a/core/hif/src/ce/ce_tasklet.c b/core/hif/src/ce/ce_tasklet.c
new file mode 100644
index 0000000..6bb550e
--- /dev/null
+++ b/core/hif/src/ce/ce_tasklet.c
@@ -0,0 +1,404 @@
+/*
+ * Copyright (c) 2015 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 <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/if_arp.h>
+#include "a_types.h"
+#include "athdefs.h"
+#include "cdf_lock.h"
+#include "cdf_types.h"
+#include "cdf_status.h"
+#include "cds_api.h"
+#include "regtable.h"
+#include "hif.h"
+#include "hif_io32.h"
+#include "ce_main.h"
+#include "ce_api.h"
+#include "ce_reg.h"
+#include "ce_internal.h"
+#ifdef CONFIG_CNSS
+#include <net/cnss.h>
+#ifdef HIF_PCI
+#include "icnss_stub.h"
+#else
+#include <soc/qcom/icnss.h>
+#endif /* HIF_PCI */
+#endif
+#include "hif_debug.h"
+#include "hif_napi.h"
+
+
+/**
+ * ce_irq_status() - read CE IRQ status
+ * @scn: struct ol_softc
+ * @ce_id: ce_id
+ * @host_status: host_status
+ *
+ * Return: IRQ status
+ */
+static inline void ce_irq_status(struct ol_softc *scn,
+ int ce_id, uint32_t *host_status)
+{
+ uint32_t offset = HOST_IS_ADDRESS + CE_BASE_ADDRESS(ce_id);
+
+ *host_status = hif_read32_mb(scn->mem + offset);
+}
+
+/**
+ * reschedule_ce_tasklet_work_handler() - reschedule work
+ * @ce_id: ce_id
+ *
+ * Return: N/A
+ */
+static void reschedule_ce_tasklet_work_handler(int ce_id)
+{
+ struct ol_softc *scn = cds_get_context(CDF_MODULE_ID_HIF);
+ struct HIF_CE_state *hif_ce_state;
+
+ if (NULL == scn) {
+ HIF_ERROR("%s: tasklet scn is null", __func__);
+ return;
+ }
+ hif_ce_state = (struct HIF_CE_state *)scn->hif_hdl;
+
+ if (scn->hif_init_done == false) {
+ HIF_ERROR("%s: wlan driver is unloaded", __func__);
+ return;
+ }
+ tasklet_schedule(&hif_ce_state->tasklets[ce_id].intr_tq);
+ return;
+}
+
+/**
+ * struct tasklet_work
+ *
+ * @id: ce_id
+ * @work: work
+ */
+struct tasklet_work {
+ enum ce_id_type id;
+ struct work_struct work;
+};
+
+static struct tasklet_work tasklet_workers[CE_ID_MAX];
+static bool work_initialized;
+
+/**
+ * work_handler() - work_handler
+ * @work: struct work_struct
+ *
+ * Return: N/A
+ */
+static void work_handler(struct work_struct *work)
+{
+ struct tasklet_work *tmp;
+
+ tmp = container_of(work, struct tasklet_work, work);
+ reschedule_ce_tasklet_work_handler(tmp->id);
+}
+
+/**
+ * init_tasklet_work() - init_tasklet_work
+ * @work: struct work_struct
+ * @work_handler: work_handler
+ *
+ * Return: N/A
+ */
+#ifdef CONFIG_CNSS
+static void init_tasklet_work(struct work_struct *work,
+ work_func_t work_handler)
+{
+ cnss_init_work(work, work_handler);
+}
+#else
+static void init_tasklet_work(struct work_struct *work,
+ work_func_t work_handler)
+{
+ INIT_WORK(work, work_handler);
+}
+#endif
+
+/**
+ * init_tasklet_workers() - init_tasklet_workers
+ *
+ * Return: N/A
+ */
+void init_tasklet_workers(void)
+{
+ uint32_t id;
+
+ for (id = 0; id < CE_ID_MAX; id++) {
+ tasklet_workers[id].id = id;
+ init_tasklet_work(&tasklet_workers[id].work, work_handler);
+ }
+ work_initialized = true;
+}
+
+#ifdef CONFIG_SLUB_DEBUG_ON
+/**
+ * ce_schedule_tasklet() - schedule ce tasklet
+ * @tasklet_entry: struct ce_tasklet_entry
+ *
+ * Return: N/A
+ */
+static inline void ce_schedule_tasklet(struct ce_tasklet_entry *tasklet_entry)
+{
+ if (work_initialized && (tasklet_entry->ce_id <= CE_ID_MAX))
+ schedule_work(&tasklet_workers[tasklet_entry->ce_id].work);
+ else
+ HIF_ERROR("%s: work_initialized = %d, ce_id = %d",
+ __func__, work_initialized, tasklet_entry->ce_id);
+}
+#else
+/**
+ * ce_schedule_tasklet() - schedule ce tasklet
+ * @tasklet_entry: struct ce_tasklet_entry
+ *
+ * Return: N/A
+ */
+static inline void ce_schedule_tasklet(struct ce_tasklet_entry *tasklet_entry)
+{
+ tasklet_schedule(&tasklet_entry->intr_tq);
+}
+#endif
+
+/**
+ * ce_tasklet() - ce_tasklet
+ * @data: data
+ *
+ * Return: N/A
+ */
+static void ce_tasklet(unsigned long data)
+{
+ struct ce_tasklet_entry *tasklet_entry =
+ (struct ce_tasklet_entry *)data;
+ struct HIF_CE_state *hif_ce_state = tasklet_entry->hif_ce_state;
+ struct ol_softc *scn = hif_ce_state->scn;
+ struct CE_state *CE_state = scn->ce_id_to_state[tasklet_entry->ce_id];
+
+ if (tasklet_entry->from_irq)
+ tasklet_entry->from_irq = false;
+
+ if (cdf_atomic_read(&scn->link_suspended)) {
+ HIF_ERROR("%s: ce %d tasklet fired after link suspend.",
+ __func__, tasklet_entry->ce_id);
+ CDF_BUG(0);
+ }
+
+ ce_per_engine_service(scn, tasklet_entry->ce_id);
+
+ if (tasklet_entry->ce_id == CE_HTT_T2H_MSG &&
+ CE_state->lro_flush_cb != NULL) {
+ CE_state->lro_flush_cb(CE_state->lro_data);
+ }
+
+ if (ce_check_rx_pending(scn, tasklet_entry->ce_id)) {
+ /*
+ * There are frames pending, schedule tasklet to process them.
+ * Enable the interrupt only when there is no pending frames in
+ * any of the Copy Engine pipes.
+ */
+ ce_schedule_tasklet(tasklet_entry);
+ return;
+ }
+
+ if (scn->target_status != OL_TRGET_STATUS_RESET)
+ ce_irq_enable(scn, tasklet_entry->ce_id);
+
+ cdf_atomic_dec(&scn->active_tasklet_cnt);
+}
+/**
+ * ce_tasklet_init() - ce_tasklet_init
+ * @hif_ce_state: hif_ce_state
+ * @mask: mask
+ *
+ * Return: N/A
+ */
+void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask)
+{
+ int i;
+
+ for (i = 0; i < CE_COUNT_MAX; i++) {
+ if (mask & (1 << i)) {
+ hif_ce_state->tasklets[i].ce_id = i;
+ hif_ce_state->tasklets[i].inited = true;
+ hif_ce_state->tasklets[i].hif_ce_state = hif_ce_state;
+ tasklet_init(&hif_ce_state->tasklets[i].intr_tq,
+ ce_tasklet,
+ (unsigned long)&hif_ce_state->tasklets[i]);
+ }
+ }
+}
+/**
+ * ce_tasklet_kill() - ce_tasklet_kill
+ * @hif_ce_state: hif_ce_state
+ *
+ * Return: N/A
+ */
+void ce_tasklet_kill(struct HIF_CE_state *hif_ce_state)
+{
+ int i;
+ struct ol_softc *scn = hif_ce_state->scn;
+
+ for (i = 0; i < CE_COUNT_MAX; i++)
+ if (hif_ce_state->tasklets[i].inited) {
+ tasklet_kill(&hif_ce_state->tasklets[i].intr_tq);
+ hif_ce_state->tasklets[i].inited = false;
+ }
+ cdf_atomic_set(&scn->active_tasklet_cnt, 0);
+}
+/**
+ * ce_irq_handler() - ce_irq_handler
+ * @ce_id: ce_id
+ * @context: context
+ *
+ * Return: N/A
+ */
+static irqreturn_t ce_irq_handler(int irq, void *context)
+{
+ struct ce_tasklet_entry *tasklet_entry = context;
+ struct HIF_CE_state *hif_ce_state = tasklet_entry->hif_ce_state;
+ struct ol_softc *scn = hif_ce_state->scn;
+ int ce_id = icnss_get_ce_id(irq);
+ uint32_t host_status;
+
+
+ if (tasklet_entry->ce_id != ce_id) {
+ HIF_ERROR("%s: ce_id (expect %d, received %d) does not match",
+ __func__, tasklet_entry->ce_id, ce_id);
+ return IRQ_NONE;
+ }
+
+#ifndef HIF_PCI
+ disable_irq_nosync(irq);
+#endif
+ ce_irq_disable(scn, ce_id);
+ ce_irq_status(scn, ce_id, &host_status);
+ cdf_atomic_inc(&scn->active_tasklet_cnt);
+ if (hif_napi_enabled(scn, ce_id))
+ hif_napi_schedule(scn, ce_id);
+ else {
+ tasklet_entry->from_irq = true;
+ tasklet_schedule(&tasklet_entry->intr_tq);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * const char *ce_name
+ *
+ * @ce_name: ce_name
+ */
+const char *ce_name[ICNSS_MAX_IRQ_REGISTRATIONS] = {
+ "WLAN_CE_0",
+ "WLAN_CE_1",
+ "WLAN_CE_2",
+ "WLAN_CE_3",
+ "WLAN_CE_4",
+ "WLAN_CE_5",
+ "WLAN_CE_6",
+ "WLAN_CE_7",
+ "WLAN_CE_8",
+ "WLAN_CE_9",
+ "WLAN_CE_10",
+ "WLAN_CE_11",
+};
+/**
+ * ce_unregister_irq() - ce_unregister_irq
+ * @hif_ce_state: hif_ce_state copy engine device handle
+ * @mask: which coppy engines to unregister for.
+ *
+ * Unregisters copy engine irqs matching mask. If a 1 is set at bit x,
+ * unregister for copy engine x.
+ *
+ * Return: CDF_STATUS
+ */
+CDF_STATUS ce_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask)
+{
+ int id;
+ int ret;
+
+ if (hif_ce_state == NULL) {
+ HIF_WARN("%s: hif_ce_state = NULL", __func__);
+ return CDF_STATUS_SUCCESS;
+ }
+ for (id = 0; id < CE_COUNT_MAX; id++) {
+ if ((mask & (1 << id)) && hif_ce_state->tasklets[id].inited) {
+ ret = icnss_ce_free_irq(id,
+ &hif_ce_state->tasklets[id]);
+ if (ret < 0)
+ HIF_ERROR(
+ "%s: icnss_unregister_irq error - ce_id = %d, ret = %d",
+ __func__, id, ret);
+ }
+ }
+ return CDF_STATUS_SUCCESS;
+}
+/**
+ * ce_register_irq() - ce_register_irq
+ * @hif_ce_state: hif_ce_state
+ * @mask: which coppy engines to unregister for.
+ *
+ * Registers copy engine irqs matching mask. If a 1 is set at bit x,
+ * Register for copy engine x.
+ *
+ * Return: CDF_STATUS
+ */
+CDF_STATUS ce_register_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask)
+{
+ int id;
+ int ret;
+ unsigned long irqflags = IRQF_TRIGGER_RISING;
+ uint32_t done_mask = 0;
+
+ for (id = 0; id < CE_COUNT_MAX; id++) {
+ if ((mask & (1 << id)) && hif_ce_state->tasklets[id].inited) {
+ ret = icnss_ce_request_irq(id, ce_irq_handler,
+ irqflags, ce_name[id],
+ &hif_ce_state->tasklets[id]);
+ if (ret) {
+ HIF_ERROR(
+ "%s: cannot register CE %d irq handler, ret = %d",
+ __func__, id, ret);
+ ce_unregister_irq(hif_ce_state, done_mask);
+ return CDF_STATUS_E_FAULT;
+ } else {
+ done_mask |= 1 << id;
+ }
+ }
+ }
+
+#ifndef HIF_PCI
+ /* move to hif_configure_irq */
+ ce_enable_irq_in_group_reg(hif_ce_state->scn, done_mask);
+#endif
+
+ return CDF_STATUS_SUCCESS;
+}
diff --git a/core/hif/src/ce/ce_tasklet.h b/core/hif/src/ce/ce_tasklet.h
new file mode 100644
index 0000000..c414770
--- /dev/null
+++ b/core/hif/src/ce/ce_tasklet.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2015 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.
+ */
+
+#ifndef __CE_TASKLET_H__
+#define __CE_TASKLET_H__
+void init_tasklet_workers(void);
+void ce_tasklet_init(struct HIF_CE_state *hif_ce_state, uint32_t mask);
+void ce_tasklet_kill(struct HIF_CE_state *hif_ce_state);
+CDF_STATUS ce_register_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
+CDF_STATUS ce_unregister_irq(struct HIF_CE_state *hif_ce_state, uint32_t mask);
+#endif /* __CE_TASKLET_H__ */