| /* |
| * Copyright (c) 2013, 2016 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| #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 */ |
| CE_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 */ |
| CE_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 hif_pci_softc *sc; /* back pointer to device's sc */ |
| unsigned int id; |
| unsigned int attr_flags; /* CE_ATTR_* */ |
| u_int32_t ctrl_addr; /* relative to BAR */ |
| enum CE_op_state state; |
| |
| 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? */ |
| u_int8_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; |
| adf_os_timer_t poll_timer; |
| }; |
| |
| /* Descriptor rings must be aligned to this boundary */ |
| #define CE_DESC_RING_ALIGN 8 |
| |
| struct CE_src_desc { |
| CE_addr_t src_ptr; |
| #if _BYTE_ORDER == _BIG_ENDIAN |
| u_int32_t meta_data:14, |
| byte_swap:1, |
| gather:1, |
| nbytes:16; |
| #else |
| |
| u_int32_t nbytes:16, |
| gather:1, |
| byte_swap:1, |
| meta_data:14; |
| #endif |
| }; |
| |
| struct dest_desc_info { |
| #if _BYTE_ORDER == _BIG_ENDIAN |
| u_int32_t meta_data:14, |
| byte_swap:1, |
| gather:1, |
| nbytes:16; |
| #else |
| u_int32_t nbytes:16, |
| gather:1, |
| byte_swap:1, |
| meta_data:14; |
| #endif |
| }; |
| |
| struct CE_dest_desc { |
| CE_addr_t dest_ptr; |
| struct dest_desc_info info; |
| }; |
| |
| /** |
| * typdef CE_desc - unified data type for ce descriptors |
| * |
| * Both src and destination descriptors follow the same format. |
| * They use different data structures for different access symantics. |
| * Here we provice a unifying data type. |
| */ |
| typedef union { |
| struct CE_src_desc src_desc; |
| struct CE_dest_desc dest_desc; |
| } CE_desc; |
| |
| #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 */ |
| u_int32_t flags; |
| } item[CE_SENDLIST_ITEMS_MAX]; |
| }; |
| |
| |
| /* 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 |
| #define CE_HW_INDEX_LINK_DOWN 0xFFFFFFFF |
| |
| /* Additional internal-only CE_send flags */ |
| #define CE_SEND_FLAG_GATHER 0x00010000 /* Use Gather */ |
| |
| #define CE_SRC_RING_WRITE_IDX_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+SR_WR_INDEX_ADDRESS, (n)) |
| |
| #define CE_SRC_RING_WRITE_IDX_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+SR_WR_INDEX_ADDRESS) |
| |
| #define CE_SRC_RING_READ_IDX_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+CURRENT_SRRI_ADDRESS) |
| |
| #define CE_SRC_RING_BASE_ADDR_SET(targid, CE_ctrl_addr, addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+SR_BA_ADDRESS, (addr)) |
| |
| #define CE_SRC_RING_SZ_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+SR_SIZE_ADDRESS, (n)) |
| |
| #define CE_SRC_RING_DMAX_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+CE_CTRL1_ADDRESS, \ |
| (A_TARGET_READ((targid), (CE_ctrl_addr)+CE_CTRL1_ADDRESS) & ~CE_CTRL1_DMAX_LENGTH_MASK) | \ |
| CE_CTRL1_DMAX_LENGTH_SET(n)) |
| #define CE_SRC_RING_BYTE_SWAP_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (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(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (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_WRITE_IDX_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+DST_WR_INDEX_ADDRESS, (n)) |
| |
| #define CE_DEST_RING_WRITE_IDX_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+DST_WR_INDEX_ADDRESS) |
| |
| #define CE_DEST_RING_READ_IDX_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+CURRENT_DRRI_ADDRESS) |
| |
| #define CE_DEST_RING_BASE_ADDR_SET(targid, CE_ctrl_addr, addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+DR_BA_ADDRESS, (addr)) |
| |
| #define CE_DEST_RING_SZ_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+DR_SIZE_ADDRESS, (n)) |
| |
| #define CE_SRC_RING_HIGHMARK_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+SRC_WATERMARK_ADDRESS, \ |
| (A_TARGET_READ((targid), (CE_ctrl_addr)+SRC_WATERMARK_ADDRESS) & ~SRC_WATERMARK_HIGH_MASK) | \ |
| SRC_WATERMARK_HIGH_SET(n)) |
| |
| #define CE_SRC_RING_LOWMARK_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+SRC_WATERMARK_ADDRESS, \ |
| (A_TARGET_READ((targid), (CE_ctrl_addr)+SRC_WATERMARK_ADDRESS) & ~SRC_WATERMARK_LOW_MASK) | \ |
| SRC_WATERMARK_LOW_SET(n)) |
| |
| #define CE_DEST_RING_HIGHMARK_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+DST_WATERMARK_ADDRESS, \ |
| (A_TARGET_READ((targid), (CE_ctrl_addr)+DST_WATERMARK_ADDRESS) & ~DST_WATERMARK_HIGH_MASK) | \ |
| DST_WATERMARK_HIGH_SET(n)) |
| |
| #define CE_DEST_RING_LOWMARK_SET(targid, CE_ctrl_addr, n) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+DST_WATERMARK_ADDRESS, \ |
| (A_TARGET_READ((targid), (CE_ctrl_addr)+DST_WATERMARK_ADDRESS) & ~DST_WATERMARK_LOW_MASK) | \ |
| DST_WATERMARK_LOW_SET(n)) |
| |
| #define CE_COPY_COMPLETE_INTR_ENABLE(targid, CE_ctrl_addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS, \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS) | HOST_IE_COPY_COMPLETE_MASK) |
| |
| #define CE_COPY_COMPLETE_INTR_DISABLE(targid, CE_ctrl_addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS, \ |
| A_TARGET_READ((targid), (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(targid, CE_ctrl_addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS, \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS) | CE_WATERMARK_MASK) |
| |
| #define CE_WATERMARK_INTR_DISABLE(targid, CE_ctrl_addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS, \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+HOST_IE_ADDRESS) & ~CE_WATERMARK_MASK) |
| |
| #define CE_ERROR_INTR_ENABLE(targid, CE_ctrl_addr) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+MISC_IE_ADDRESS, \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+MISC_IE_ADDRESS) | CE_ERROR_MASK) |
| |
| #define CE_MISC_INT_STATUS_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+MISC_IS_ADDRESS) |
| |
| #define CE_ENGINE_INT_STATUS_GET(targid, CE_ctrl_addr) \ |
| A_TARGET_READ((targid), (CE_ctrl_addr)+HOST_IS_ADDRESS) |
| |
| #define CE_ENGINE_INT_STATUS_CLEAR(targid, CE_ctrl_addr, mask) \ |
| A_TARGET_WRITE((targid), (CE_ctrl_addr)+HOST_IS_ADDRESS, (mask)) |
| |
| #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_INTERRUPT_SUMMARY(targid) \ |
| CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( \ |
| A_TARGET_READ((targid), CE_WRAPPER_BASE_ADDRESS+CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)) |
| |
| /*Macro to increment CE packet errors*/ |
| #define OL_ATH_CE_PKT_ERROR_COUNT_INCR(_sc,_ce_ecode);\ |
| {\ |
| if(_ce_ecode==CE_RING_DELTA_FAIL)(_sc->ol_sc->pkt_stats.ce_ring_delta_fail_count)+=1;\ |
| } |
| |
| |
| /* 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) |