diff --git a/hif/src/ce/ce_api.h b/hif/src/ce/ce_api.h index 6098876b500e..d11354163f0f 100644 --- a/hif/src/ce/ce_api.h +++ b/hif/src/ce/ce_api.h @@ -484,4 +484,60 @@ int ce_lro_flush_cb_register(struct hif_opaque_softc *scn, void (handler)(void *), void *data); int ce_lro_flush_cb_deregister(struct hif_opaque_softc *scn); #endif +struct ce_ops *ce_services_srng(void); +struct ce_ops *ce_services_legacy(void); +bool ce_srng_based(struct hif_softc *scn); +/* Forward declaration */ +struct CE_ring_state; + +struct ce_ops { + uint32_t (*ce_get_desc_size)(uint8_t ring_type); + void (*ce_ring_setup)(struct hif_softc *scn, uint8_t ring_type, + uint32_t ce_id, struct CE_ring_state *ring, + struct CE_attr *attr); + int (*ce_send_nolock)(struct CE_handle *copyeng, + void *per_transfer_context, + qdf_dma_addr_t buffer, + uint32_t nbytes, + uint32_t transfer_id, + uint32_t flags, + uint32_t user_flags); + int (*ce_sendlist_send)(struct CE_handle *copyeng, + void *per_transfer_context, + struct ce_sendlist *sendlist, unsigned int transfer_id); + QDF_STATUS (*ce_revoke_recv_next)(struct CE_handle *copyeng, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_dma_addr_t *bufferp); + QDF_STATUS (*ce_cancel_send_next)(struct CE_handle *copyeng, + void **per_CE_contextp, void **per_transfer_contextp, + qdf_dma_addr_t *bufferp, unsigned int *nbytesp, + unsigned int *transfer_idp, + uint32_t *toeplitz_hash_result); + int (*ce_recv_buf_enqueue)(struct CE_handle *copyeng, + void *per_recv_context, qdf_dma_addr_t buffer); + bool (*watermark_int)(struct CE_state *CE_state, unsigned int *flags); + int (*ce_completed_recv_next_nolock)(struct CE_state *CE_state, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_dma_addr_t *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *flagsp); + int (*ce_completed_send_next_nolock)(struct CE_state *CE_state, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_dma_addr_t *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + unsigned int *sw_idx, + unsigned int *hw_idx, + uint32_t *toeplitz_hash_result); + unsigned int (*ce_recv_entries_done_nolock)(struct hif_softc *scn, + struct CE_state *CE_state); + unsigned int (*ce_send_entries_done_nolock)(struct hif_softc *scn, + struct CE_state *CE_state); + void (*ce_per_engine_handler_adjust)(struct CE_state *CE_state, + int disable_copy_compl_intr); +}; #endif /* __COPY_ENGINE_API_H__ */ diff --git a/hif/src/ce/ce_assignment.h b/hif/src/ce/ce_assignment.h index 6d1d853e1cef..75a0d2c3ba2d 100644 --- a/hif/src/ce/ce_assignment.h +++ b/hif/src/ce/ce_assignment.h @@ -402,4 +402,67 @@ static struct CE_pipe_config target_ce_config_wlan_ar900b[] = { #endif }; +#ifdef QCA_WIFI_QCA8074 +static struct CE_attr host_ce_config_wlan_qca8074[] = { + /* 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,}, + /* Target to uMC */ + { /* CE8 */ CE_ATTR_FLAGS, 0, 0, 0, 0, NULL,}, + /* target->host HTT */ + { /* CE9 */ CE_ATTR_FLAGS, 0, 0, 2048, 512, NULL,}, + /* target->host HTT */ + { /* CE10 */ CE_ATTR_FLAGS, 0, 0, 2048, 512, NULL,}, + /* target -> host PKTLOG */ + { /* CE11 */ CE_ATTR_FLAGS, 0, 0, 2048, 512, NULL,}, +}; + +static struct CE_pipe_config target_ce_config_wlan_qca8074[] = { + /* 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, 16384, 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,}, + /* CE9 target->host HTT */ + { /* CE9 */ 9, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* CE10 target->host HTT */ + { /* CE10 */ 10, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, + /* Target -> host PKTLOG */ + { /* CE11 */ 11, PIPEDIR_IN, 32, 2048, CE_ATTR_FLAGS, 0,}, +}; +#endif + #endif /* __HIF_PCI_INTERNAL_H__ */ diff --git a/hif/src/ce/ce_internal.h b/hif/src/ce/ce_internal.h index 178aa9ad30e9..f90b3f6a5ae1 100644 --- a/hif/src/ce/ce_internal.h +++ b/hif/src/ce/ce_internal.h @@ -94,6 +94,7 @@ struct CE_ring_state { unsigned int low_water_mark_nentries; unsigned int high_water_mark_nentries; + void *srng_ctx; void **per_transfer_context; OS_DMA_MEM_CONTEXT(ce_dmacontext) /* OS Specific DMA context */ }; @@ -129,6 +130,7 @@ struct CE_state { unsigned int src_sz_max; struct CE_ring_state *src_ring; struct CE_ring_state *dest_ring; + struct CE_ring_state *status_ring; atomic_t rx_pending; qdf_spinlock_t ce_index_lock; @@ -288,6 +290,85 @@ struct CE_dest_desc { }; #endif /* QCA_WIFI_3_0 */ +#ifdef QCA_WIFI_QCA8074 +struct ce_srng_src_desc { + uint32_t buffer_addr_lo; +#if _BYTE_ORDER == _BIG_ENDIAN + uint32_t nbytes:16, + rsvd:4, + gather:1, + dest_swap:1, + byte_swap:1, + toeplitz_hash_enable:1, + buffer_addr_hi:8; + uint32_t rsvd1:16, + meta_data:16; + uint32_t loop_count:4, + ring_id:8, + rsvd3:20; +#else + uint32_t buffer_addr_hi:8, + toeplitz_hash_enable:1, + byte_swap:1, + dest_swap:1, + gather:1, + rsvd:4, + nbytes:16; + uint32_t meta_data:16, + rsvd1:16; + uint32_t rsvd3:20, + ring_id:8, + loop_count:4; +#endif +}; +struct ce_srng_dest_desc { + uint32_t buffer_addr_lo; +#if _BYTE_ORDER == _BIG_ENDIAN + uint32_t loop_count:4, + ring_id:8, + rsvd1:12, + buffer_addr_hi:8; +#else + uint32_t buffer_addr_hi:8, + rsvd1:12, + ring_id:8, + loop_count:4; +#endif +}; +struct ce_srng_dest_status_desc { +#if _BYTE_ORDER == _BIG_ENDIAN + uint32_t nbytes:16, + rsvd:4, + gather:1, + dest_swap:1, + byte_swap:1, + toeplitz_hash_enable:1, + rsvd0:8; + uint32_t rsvd1:16, + meta_data:16; +#else + uint32_t rsvd0:8, + toeplitz_hash_enable:1, + byte_swap:1, + dest_swap:1, + gather:1, + rsvd:4, + nbytes:16; + uint32_t meta_data:16, + rsvd1:16; +#endif + uint32_t toeplitz_hash; +#if _BYTE_ORDER == _BIG_ENDIAN + uint32_t loop_count:4, + ring_id:8, + rsvd3:20; +#else + uint32_t rsvd3:20, + ring_id:8, + loop_count:4; +#endif +}; +#endif #define CE_SENDLIST_ITEMS_MAX 12 /** @@ -407,6 +488,7 @@ static inline void ce_t2h_msg_ce_cleanup(struct CE_handle *ce_hdl) /* which ring of a CE? */ #define CE_RING_SRC 0 #define CE_RING_DEST 1 +#define CE_RING_STATUS 2 #define CDC_WAR_MAGIC_STR 0xceef0000 #define CDC_WAR_DATA_CE 4 diff --git a/hif/src/ce/ce_main.c b/hif/src/ce/ce_main.c index 681e962ba52f..b8a31e64edb9 100644 --- a/hif/src/ce/ce_main.c +++ b/hif/src/ce/ce_main.c @@ -586,6 +586,136 @@ static void ce_ring_test_initial_indexes(int ce_id, struct CE_ring_state *ring, QDF_BUG(0); } +/** + * ce_srng_based() - Does this target use srng + * @ce_state : pointer to the state context of the CE + * + * Description: + * returns true if the target is SRNG based + * + * Return: + * false (attribute set to false) + * true (attribute set to true); + */ +bool ce_srng_based(struct hif_softc *scn) +{ + struct hif_opaque_softc *hif_hdl = GET_HIF_OPAQUE_HDL(scn); + struct hif_target_info *tgt_info = hif_get_target_info_handle(hif_hdl); + + switch (tgt_info->target_type) { +#ifdef QCA_WIFI_QCA8074 + case TARGET_TYPE_QCA8074: + return true; +#endif + default: + return false; + } + return false; +} + +struct ce_ops *ce_services_attach(struct hif_softc *scn) +{ +#ifdef QCA_WIFI_QCA8074 + if (ce_srng_based(scn)) + return ce_services_srng(); +#endif + + return ce_services_legacy(); +} + +static inline uint32_t ce_get_desc_size(struct hif_softc *scn, + uint8_t ring_type) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + + return hif_state->ce_services->ce_get_desc_size(ring_type); +} + + +struct CE_ring_state *ce_alloc_ring_state(struct CE_state *CE_state, + uint8_t ring_type, uint32_t nentries) +{ + uint32_t ce_nbytes; + char *ptr; + qdf_dma_addr_t base_addr; + struct CE_ring_state *ce_ring; + uint32_t desc_size; + struct hif_softc *scn = CE_state->scn; + + ce_nbytes = sizeof(struct CE_ring_state) + + (nentries * sizeof(void *)); + ptr = qdf_mem_malloc(ce_nbytes); + if (!ptr) + return NULL; + + qdf_mem_zero(ptr, ce_nbytes); + + ce_ring = (struct CE_ring_state *)ptr; + ptr += sizeof(struct CE_ring_state); + ce_ring->nentries = nentries; + ce_ring->nentries_mask = nentries - 1; + + ce_ring->low_water_mark_nentries = 0; + ce_ring->high_water_mark_nentries = nentries; + ce_ring->per_transfer_context = (void **)ptr; + + desc_size = ce_get_desc_size(scn, ring_type); + + /* Legacy platforms that do not support cache + * coherent DMA are unsupported + */ + ce_ring->base_addr_owner_space_unaligned = + qdf_mem_alloc_consistent(scn->qdf_dev, + scn->qdf_dev->dev, + (nentries * + desc_size + + CE_DESC_RING_ALIGN), + &base_addr); + if (ce_ring->base_addr_owner_space_unaligned + == NULL) { + HIF_ERROR("%s: ring has no DMA mem", + __func__); + qdf_mem_free(ptr); + return NULL; + } + ce_ring->base_addr_CE_space_unaligned = base_addr; + + /* Correctly initialize memory to 0 to + * prevent garbage data crashing system + * when download firmware + */ + qdf_mem_zero(ce_ring->base_addr_owner_space_unaligned, + nentries * desc_size + + CE_DESC_RING_ALIGN); + + if (ce_ring->base_addr_CE_space_unaligned & (CE_DESC_RING_ALIGN - 1)) { + + ce_ring->base_addr_CE_space = + (ce_ring->base_addr_CE_space_unaligned + + CE_DESC_RING_ALIGN - 1) & ~(CE_DESC_RING_ALIGN - 1); + + ce_ring->base_addr_owner_space = (void *) + (((size_t) ce_ring->base_addr_owner_space_unaligned + + CE_DESC_RING_ALIGN - 1) & ~(CE_DESC_RING_ALIGN - 1)); + } else { + ce_ring->base_addr_CE_space = + ce_ring->base_addr_CE_space_unaligned; + ce_ring->base_addr_owner_space = + ce_ring->base_addr_owner_space_unaligned; + } + + return ce_ring; +} + +static void ce_ring_setup(struct hif_softc *scn, uint8_t ring_type, + uint32_t ce_id, struct CE_ring_state *ring, + struct CE_attr *attr) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + + hif_state->ce_services->ce_ring_setup(scn, ring_type, ce_id, ring, attr); +} + /* * Initialize a Copy Engine based on caller-supplied attributes. * This may be called once to initialize both source and destination @@ -602,7 +732,6 @@ struct CE_handle *ce_init(struct hif_softc *scn, struct CE_state *CE_state; uint32_t ctrl_addr; unsigned int nentries; - qdf_dma_addr_t base_addr; bool malloc_CE_state = false; bool malloc_src_ring = false; @@ -647,17 +776,15 @@ struct CE_handle *ce_init(struct hif_softc *scn, 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) { QDF_ASSERT(CE_state->src_ring->nentries == nentries); } else { - CE_nbytes = sizeof(struct CE_ring_state) - + (nentries * sizeof(void *)); - ptr = qdf_mem_malloc(CE_nbytes); - if (!ptr) { + src_ring = CE_state->src_ring = + ce_alloc_ring_state(CE_state, + CE_RING_SRC, + nentries); + if (!src_ring) { /* cannot allocate src ring. If the * CE_state is allocated locally free * CE_State and return error. @@ -677,72 +804,6 @@ struct CE_handle *ce_init(struct hif_softc *scn, */ malloc_src_ring = true; } - qdf_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; - if (Q_TARGET_ACCESS_BEGIN(scn) < 0) - goto error_target_access; - src_ring->hw_index = - CE_SRC_RING_READ_IDX_GET_FROM_REGISTER(scn, - ctrl_addr); - src_ring->sw_index = src_ring->hw_index; - src_ring->write_index = - CE_SRC_RING_WRITE_IDX_GET_FROM_REGISTER(scn, - ctrl_addr); - - ce_ring_test_initial_indexes(CE_id, src_ring, - "src_ring"); - - if (Q_TARGET_ACCESS_END(scn) < 0) - goto error_target_access; - - 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 = - qdf_mem_alloc_consistent(scn->qdf_dev, - scn->qdf_dev->dev, - (nentries * - sizeof(struct CE_src_desc) + - CE_DESC_RING_ALIGN), - &base_addr); - 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. @@ -763,30 +824,13 @@ struct CE_handle *ce_init(struct hif_softc *scn, if (Q_TARGET_ACCESS_BEGIN(scn) < 0) goto error_target_access; - dma_addr = src_ring->base_addr_CE_space; - CE_SRC_RING_BASE_ADDR_SET(scn, ctrl_addr, - (uint32_t)(dma_addr & 0xFFFFFFFF)); - /* if SR_BA_ADDRESS_HIGH register exists */ - if (is_register_supported(SR_BA_ADDRESS_HIGH)) { - 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); - } - 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); + ce_ring_setup(scn, CE_RING_SRC, CE_id, src_ring, attr); + if (Q_TARGET_ACCESS_END(scn) < 0) goto error_target_access; + ce_ring_test_initial_indexes(CE_id, src_ring, + "src_ring"); } } @@ -794,18 +838,16 @@ struct CE_handle *ce_init(struct hif_softc *scn, 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) { QDF_ASSERT(CE_state->dest_ring->nentries == nentries); } else { - CE_nbytes = sizeof(struct CE_ring_state) - + (nentries * sizeof(void *)); - ptr = qdf_mem_malloc(CE_nbytes); - if (!ptr) { + dest_ring = CE_state->dest_ring = + ce_alloc_ring_state(CE_state, + CE_RING_DEST, + nentries); + if (!dest_ring) { /* cannot allocate dst ring. If the CE_state * or src ring is allocated locally free * CE_State and src ring and return error. @@ -825,108 +867,55 @@ struct CE_handle *ce_init(struct hif_softc *scn, } return NULL; } - qdf_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; if (Q_TARGET_ACCESS_BEGIN(scn) < 0) goto error_target_access; - dest_ring->sw_index = - CE_DEST_RING_READ_IDX_GET_FROM_REGISTER(scn, - ctrl_addr); - dest_ring->write_index = - CE_DEST_RING_WRITE_IDX_GET_FROM_REGISTER(scn, - ctrl_addr); + + ce_ring_setup(scn, CE_RING_DEST, CE_id, dest_ring, attr); + + if (Q_TARGET_ACCESS_END(scn) < 0) + goto error_target_access; ce_ring_test_initial_indexes(CE_id, dest_ring, "dest_ring"); - if (Q_TARGET_ACCESS_END(scn) < 0) - goto error_target_access; +#ifdef QCA_WIFI_QCA8074 + /* For srng based target, init status ring here */ + if (ce_srng_based(CE_state->scn)) { + CE_state->status_ring = + ce_alloc_ring_state(CE_state, + CE_RING_STATUS, + nentries); + if (CE_state->status_ring == NULL) { + /*Allocation failed. Cleanup*/ + qdf_mem_free(CE_state->dest_ring); + if (malloc_src_ring) { + qdf_mem_free + (CE_state->src_ring); + CE_state->src_ring = NULL; + malloc_src_ring = false; + } + if (malloc_CE_state) { + /* allocated CE_state locally */ + scn->ce_id_to_state[CE_id] = + NULL; + qdf_mem_free(CE_state); + malloc_CE_state = false; + } - dest_ring->low_water_mark_nentries = 0; - dest_ring->high_water_mark_nentries = nentries; - dest_ring->per_transfer_context = (void **)ptr; + return NULL; + } + if (Q_TARGET_ACCESS_BEGIN(scn) < 0) + goto error_target_access; + + ce_ring_setup(scn, CE_RING_STATUS, CE_id, + CE_state->status_ring, attr); + + if (Q_TARGET_ACCESS_END(scn) < 0) + goto error_target_access; - /* Legacy platforms that do not support cache - * coherent DMA are unsupported */ - dest_ring->base_addr_owner_space_unaligned = - qdf_mem_alloc_consistent(scn->qdf_dev, - scn->qdf_dev->dev, - (nentries * - sizeof(struct CE_dest_desc) + - CE_DESC_RING_ALIGN), - &base_addr); - 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 - */ - qdf_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; - } - - if (Q_TARGET_ACCESS_BEGIN(scn) < 0) - goto error_target_access; - dma_addr = dest_ring->base_addr_CE_space; - CE_DEST_RING_BASE_ADDR_SET(scn, ctrl_addr, - (uint32_t)(dma_addr & 0xFFFFFFFF)); - - /* if DR_BA_ADDRESS_HIGH exists */ - if (is_register_supported(DR_BA_ADDRESS_HIGH)) { - 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); - } - - 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); - if (Q_TARGET_ACCESS_END(scn) < 0) - goto error_target_access; - /* epping */ /* poll timer */ if ((CE_state->attr_flags & CE_ATTR_ENABLE_POLL)) { @@ -942,12 +931,14 @@ struct CE_handle *ce_init(struct hif_softc *scn, } } - /* Enable CE error interrupts */ - if (Q_TARGET_ACCESS_BEGIN(scn) < 0) - goto error_target_access; - CE_ERROR_INTR_ENABLE(scn, ctrl_addr); - if (Q_TARGET_ACCESS_END(scn) < 0) - goto error_target_access; + if (!ce_srng_based(scn)) { + /* Enable CE error interrupts */ + if (Q_TARGET_ACCESS_BEGIN(scn) < 0) + goto error_target_access; + CE_ERROR_INTR_ENABLE(scn, ctrl_addr); + if (Q_TARGET_ACCESS_END(scn) < 0) + goto error_target_access; + } /* update the htt_data attribute */ ce_mark_datapath(CE_state); @@ -1182,6 +1173,28 @@ void ce_fini(struct CE_handle *copyeng) qdf_timer_free(&CE_state->poll_timer); } } +#ifdef QCA_WIFI_QCA8074 + if (CE_state->status_ring) { + /* Cleanup the datapath Tx ring */ + ce_h2t_tx_ce_cleanup(copyeng); + + if (CE_state->status_ring->shadow_base_unaligned) + qdf_mem_free( + CE_state->status_ring->shadow_base_unaligned); + + if (CE_state->status_ring->base_addr_owner_space_unaligned) + qdf_mem_free_consistent(scn->qdf_dev, + scn->qdf_dev->dev, + (CE_state->status_ring->nentries * + sizeof(struct CE_src_desc) + + CE_DESC_RING_ALIGN), + CE_state->status_ring-> + base_addr_owner_space_unaligned, + CE_state->status_ring-> + base_addr_CE_space, 0); + qdf_mem_free(CE_state->status_ring); + } +#endif qdf_mem_free(CE_state); } @@ -2042,6 +2055,14 @@ void hif_ce_prepare_config(struct hif_softc *scn) target_service_to_ce_map_sz = sizeof(target_service_to_ce_map_ar900b); break; +#ifdef QCA_WIFI_QCA8074 + case TARGET_TYPE_QCA8074: + hif_state->host_ce_config = host_ce_config_wlan_qca8074; + hif_state->target_ce_config = target_ce_config_wlan_qca8074; + hif_state->target_ce_config_sz = + sizeof(target_ce_config_wlan_qca8074); + break; +#endif } } @@ -2162,6 +2183,7 @@ int hif_config_ce(struct hif_softc *scn) hif_config_rri_on_ddr(scn); + hif_state->ce_services = ce_services_attach(scn); /* During CE initializtion */ scn->ce_count = HOST_CE_COUNT; for (pipe_num = 0; pipe_num < scn->ce_count; pipe_num++) { @@ -2195,6 +2217,9 @@ int hif_config_ce(struct hif_softc *scn) if (attr->dest_nentries > 0) { atomic_set(&pipe_info->recv_bufs_needed, init_buffer_count(attr->dest_nentries - 1)); + /*SRNG based CE has one entry less */ + if (ce_srng_based(scn)) + atomic_dec(&pipe_info->recv_bufs_needed); } else { atomic_set(&pipe_info->recv_bufs_needed, 0); } diff --git a/hif/src/ce/ce_main.h b/hif/src/ce/ce_main.h index fbc0f6c5c3e3..9f322d298193 100644 --- a/hif/src/ce/ce_main.h +++ b/hif/src/ce/ce_main.h @@ -160,6 +160,7 @@ struct HIF_CE_state { /* Copy Engine used for Diagnostic Accesses */ struct CE_handle *ce_diag; struct ce_intr_stats stats; + struct ce_ops *ce_services; }; /* diff --git a/hif/src/ce/ce_service.c b/hif/src/ce/ce_service.c index b6927c74a543..08b4978ff047 100644 --- a/hif/src/ce/ce_service.c +++ b/hif/src/ce/ce_service.c @@ -249,15 +249,6 @@ bool hif_ce_service_should_yield(struct hif_softc *scn, * 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, - qdf_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 hif_softc *scn, u32 ctrl_addr, unsigned int write_index) @@ -316,7 +307,7 @@ static void ce_validate_nbytes(uint32_t nbytes, struct CE_state *ce_state) #endif int -ce_send_nolock(struct CE_handle *copyeng, +ce_send_nolock_legacy(struct CE_handle *copyeng, void *per_transfer_context, qdf_dma_addr_t buffer, uint32_t nbytes, @@ -421,9 +412,11 @@ ce_send(struct CE_handle *copyeng, { struct CE_state *CE_state = (struct CE_state *)copyeng; int status; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(CE_state->scn); qdf_spin_lock_bh(&CE_state->ce_index_lock); - status = ce_send_nolock(copyeng, per_transfer_context, buffer, nbytes, + status = hif_state->ce_services->ce_send_nolock(copyeng, + per_transfer_context, buffer, nbytes, transfer_id, flags, user_flag); qdf_spin_unlock_bh(&CE_state->ce_index_lock); @@ -471,6 +464,17 @@ int ce_sendlist_send(struct CE_handle *copyeng, void *per_transfer_context, struct ce_sendlist *sendlist, unsigned int transfer_id) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(CE_state->scn); + + return hif_state->ce_services->ce_sendlist_send(copyeng, + per_transfer_context, sendlist, transfer_id); +} +int +ce_sendlist_send_legacy(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; @@ -497,7 +501,8 @@ ce_sendlist_send(struct CE_handle *copyeng, item = &sl->item[i]; /* TBDXXX: Support extensible sendlist_types? */ QDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE); - status = ce_send_nolock(copyeng, CE_SENDLIST_ITEM_CTXT, + status = ce_send_nolock_legacy(copyeng, + CE_SENDLIST_ITEM_CTXT, (qdf_dma_addr_t) item->data, item->u.nbytes, transfer_id, item->flags | CE_SEND_FLAG_GATHER, @@ -508,7 +513,7 @@ ce_sendlist_send(struct CE_handle *copyeng, item = &sl->item[i]; /* TBDXXX: Support extensible sendlist_types? */ QDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE); - status = ce_send_nolock(copyeng, per_transfer_context, + status = ce_send_nolock_legacy(copyeng, per_transfer_context, (qdf_dma_addr_t) item->data, item->u.nbytes, transfer_id, item->flags, @@ -950,6 +955,7 @@ int ce_send_single(struct CE_handle *ce_tx_hdl, qdf_nbuf_t msdu, return QDF_STATUS_SUCCESS; } + /** * ce_recv_buf_enqueue() - enqueue a recv buffer into a copy engine * @coyeng: copy engine handle @@ -961,6 +967,25 @@ int ce_send_single(struct CE_handle *ce_tx_hdl, qdf_nbuf_t msdu, int ce_recv_buf_enqueue(struct CE_handle *copyeng, void *per_recv_context, qdf_dma_addr_t buffer) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(CE_state->scn); + + return hif_state->ce_services->ce_recv_buf_enqueue(copyeng, + per_recv_context, buffer); +} + +/** + * ce_recv_buf_enqueue_legacy() - enqueue a recv buffer into a copy engine + * @coyeng: copy engine handle + * @per_recv_context: virtual address of the nbuf + * @buffer: physical address of the nbuf + * + * Return: 0 if the buffer is enqueued + */ +int +ce_recv_buf_enqueue_legacy(struct CE_handle *copyeng, + void *per_recv_context, qdf_dma_addr_t buffer) { int status; struct CE_state *CE_state = (struct CE_state *)copyeng; @@ -1083,7 +1108,7 @@ unsigned int ce_recv_entries_avail(struct CE_handle *copyeng) * The caller takes responsibility for any necessary locking. */ unsigned int -ce_send_entries_done_nolock(struct hif_softc *scn, +ce_send_entries_done_nolock_legacy(struct hif_softc *scn, struct CE_state *CE_state) { struct CE_ring_state *src_ring = CE_state->src_ring; @@ -1102,9 +1127,12 @@ unsigned int ce_send_entries_done(struct CE_handle *copyeng) { struct CE_state *CE_state = (struct CE_state *)copyeng; unsigned int nentries; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); qdf_spin_lock(&CE_state->ce_index_lock); - nentries = ce_send_entries_done_nolock(CE_state->scn, CE_state); + nentries = hif_state->ce_services->ce_send_entries_done_nolock( + CE_state->scn, CE_state); qdf_spin_unlock(&CE_state->ce_index_lock); return nentries; @@ -1115,7 +1143,7 @@ unsigned int ce_send_entries_done(struct CE_handle *copyeng) * The caller takes responsibility for any necessary locking. */ unsigned int -ce_recv_entries_done_nolock(struct hif_softc *scn, +ce_recv_entries_done_nolock_legacy(struct hif_softc *scn, struct CE_state *CE_state) { struct CE_ring_state *dest_ring = CE_state->dest_ring; @@ -1134,9 +1162,12 @@ unsigned int ce_recv_entries_done(struct CE_handle *copyeng) { struct CE_state *CE_state = (struct CE_state *)copyeng; unsigned int nentries; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); qdf_spin_lock(&CE_state->ce_index_lock); - nentries = ce_recv_entries_done_nolock(CE_state->scn, CE_state); + nentries = hif_state->ce_services->ce_recv_entries_done_nolock( + CE_state->scn, CE_state); qdf_spin_unlock(&CE_state->ce_index_lock); return nentries; @@ -1153,7 +1184,7 @@ void *ce_debug_cmplsn_context; /* completed send next context */ * The caller takes responsibility for any necessary locking. */ int -ce_completed_recv_next_nolock(struct CE_state *CE_state, +ce_completed_recv_next_nolock_legacy(struct CE_state *CE_state, void **per_CE_contextp, void **per_transfer_contextp, qdf_dma_addr_t *bufferp, @@ -1231,22 +1262,37 @@ ce_completed_recv_next(struct CE_handle *copyeng, { struct CE_state *CE_state = (struct CE_state *)copyeng; int status; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + struct ce_ops *ce_services; + ce_services = hif_state->ce_services; qdf_spin_lock_bh(&CE_state->ce_index_lock); status = - ce_completed_recv_next_nolock(CE_state, per_CE_contextp, - per_transfer_contextp, bufferp, + ce_services->ce_completed_recv_next_nolock(CE_state, + per_CE_contextp, per_transfer_contextp, bufferp, nbytesp, transfer_idp, flagsp); qdf_spin_unlock_bh(&CE_state->ce_index_lock); return status; } -/* NB: Modeled after ce_completed_recv_next_nolock */ QDF_STATUS ce_revoke_recv_next(struct CE_handle *copyeng, void **per_CE_contextp, void **per_transfer_contextp, qdf_dma_addr_t *bufferp) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(CE_state->scn); + + return hif_state->ce_services->ce_revoke_recv_next(copyeng, + per_CE_contextp, per_transfer_contextp, bufferp); +} +/* NB: Modeled after ce_completed_recv_next_nolock */ +QDF_STATUS +ce_revoke_recv_next_legacy(struct CE_handle *copyeng, + void **per_CE_contextp, + void **per_transfer_contextp, qdf_dma_addr_t *bufferp) { struct CE_state *CE_state; struct CE_ring_state *dest_ring; @@ -1305,7 +1351,7 @@ ce_revoke_recv_next(struct CE_handle *copyeng, * The caller takes responsibility for any necessary locking. */ int -ce_completed_send_next_nolock(struct CE_state *CE_state, +ce_completed_send_next_nolock_legacy(struct CE_state *CE_state, void **per_CE_contextp, void **per_transfer_contextp, qdf_dma_addr_t *bufferp, @@ -1392,7 +1438,6 @@ ce_completed_send_next_nolock(struct CE_state *CE_state, return status; } -/* NB: Modeled after ce_completed_send_next */ QDF_STATUS ce_cancel_send_next(struct CE_handle *copyeng, void **per_CE_contextp, @@ -1401,6 +1446,24 @@ ce_cancel_send_next(struct CE_handle *copyeng, unsigned int *nbytesp, unsigned int *transfer_idp, uint32_t *toeplitz_hash_result) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(CE_state->scn); + + return hif_state->ce_services->ce_cancel_send_next + (copyeng, per_CE_contextp, per_transfer_contextp, + bufferp, nbytesp, transfer_idp, toeplitz_hash_result); +} + +/* NB: Modeled after ce_completed_send_next */ +QDF_STATUS +ce_cancel_send_next_legacy(struct CE_handle *copyeng, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_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; @@ -1476,13 +1539,17 @@ ce_completed_send_next(struct CE_handle *copyeng, unsigned int *toeplitz_hash_result) { struct CE_state *CE_state = (struct CE_state *)copyeng; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + struct ce_ops *ce_services; int status; + ce_services = hif_state->ce_services; qdf_spin_lock_bh(&CE_state->ce_index_lock); status = - ce_completed_send_next_nolock(CE_state, per_CE_contextp, - per_transfer_contextp, bufferp, - nbytesp, transfer_idp, sw_idx, + ce_services->ce_completed_send_next_nolock(CE_state, + per_CE_contextp, per_transfer_contextp, + bufferp, nbytesp, transfer_idp, sw_idx, hw_idx, toeplitz_hash_result); qdf_spin_unlock_bh(&CE_state->ce_index_lock); @@ -1509,6 +1576,7 @@ void ce_per_engine_servicereap(struct hif_softc *scn, unsigned int ce_id) unsigned int sw_idx, hw_idx; uint32_t toeplitz_hash_result; struct CE_state *CE_state = scn->ce_id_to_state[ce_id]; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); if (Q_TARGET_ACCESS_BEGIN(scn) < 0) return; @@ -1533,10 +1601,11 @@ void ce_per_engine_servicereap(struct hif_softc *scn, unsigned int ce_id) if (CE_state->send_cb) { { + struct ce_ops *ce_services = hif_state->ce_services; /* Pop completed send buffers and call the * registered send callback for each */ - while (ce_completed_send_next_nolock + while (ce_services->ce_completed_send_next_nolock (CE_state, &CE_context, &transfer_context, &buf, &nbytes, &id, &sw_idx, &hw_idx, @@ -1768,7 +1837,7 @@ more_data: CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, HOST_IS_COPY_COMPLETE_MASK); - if (ce_recv_entries_done_nolock(scn, ce_state)) { + if (ce_recv_entries_done_nolock_legacy(scn, ce_state)) { if (more_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) { goto more_data; } else { @@ -1799,13 +1868,13 @@ int ce_per_engine_service(struct hif_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; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); void *CE_context; void *transfer_context; qdf_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; @@ -1844,7 +1913,7 @@ more_completions: /* Pop completed recv buffers and call * the registered recv callback for each */ - while (ce_completed_recv_next_nolock + while (hif_state->ce_services->ce_completed_recv_next_nolock (CE_state, &CE_context, &transfer_context, &buf, &nbytes, &id, &flags) == QDF_STATUS_SUCCESS) { @@ -1890,7 +1959,7 @@ more_completions: */ #ifdef ATH_11AC_TXCOMPACT - while (ce_completed_send_next_nolock + while (hif_state->ce_services->ce_completed_send_next_nolock (CE_state, &CE_context, &transfer_context, &buf, &nbytes, &id, &sw_idx, &hw_idx, @@ -1916,7 +1985,7 @@ more_completions: } } #else /*ATH_11AC_TXCOMPACT */ - while (ce_completed_send_next_nolock + while (hif_state->ce_services->ce_completed_send_next_nolock (CE_state, &CE_context, &transfer_context, &buf, &nbytes, &id, &sw_idx, &hw_idx, @@ -1933,20 +2002,14 @@ more_completions: 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) { - qdf_spin_unlock(&CE_state->ce_index_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); - qdf_spin_lock(&CE_state->ce_index_lock); - } + if (CE_state->watermark_cb && + hif_state->ce_services->watermark_int(CE_state, + &flags)) { + qdf_spin_unlock(&CE_state->ce_index_lock); + /* Convert HW IS bits to software flags */ + CE_state->watermark_cb((struct CE_handle *)CE_state, + CE_state->wm_context, flags); + qdf_spin_lock(&CE_state->ce_index_lock); } } @@ -1957,7 +2020,8 @@ more_watermarks: * more copy completions happened while the misc interrupts were being * handled. */ - CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, + if (!ce_srng_based(scn)) + CE_ENGINE_INT_STATUS_CLEAR(scn, ctrl_addr, CE_WATERMARK_MASK | HOST_IS_COPY_COMPLETE_MASK); @@ -1968,7 +2032,9 @@ more_watermarks: * 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 (CE_state->recv_cb && + hif_state->ce_services->ce_recv_entries_done_nolock(scn, + CE_state)) { if (QDF_IS_EPPING_ENABLED(mode) || more_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) { goto more_completions; @@ -1982,7 +2048,9 @@ more_watermarks: } } - if (CE_state->send_cb && ce_send_entries_done_nolock(scn, CE_state)) { + if (CE_state->send_cb && + hif_state->ce_services->ce_send_entries_done_nolock(scn, + CE_state)) { if (QDF_IS_EPPING_ENABLED(mode) || more_snd_comp_cnt++ < CE_TXRX_COMP_CHECK_THRESHOLD) { goto more_completions; @@ -1996,13 +2064,9 @@ 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) { - goto more_watermarks; - } - } + if (CE_state->misc_cbs && CE_state->watermark_cb) { + if (hif_state->ce_services->watermark_int(CE_state, &flags)) + goto more_watermarks; } qdf_atomic_set(&CE_state->rx_pending, 0); @@ -2065,7 +2129,7 @@ void ce_per_engine_service_any(int irq, struct hif_softc *scn) * Called with target_lock held. */ static void -ce_per_engine_handler_adjust(struct CE_state *CE_state, +ce_per_engine_handler_adjust_legacy(struct CE_state *CE_state, int disable_copy_compl_intr) { uint32_t ctrl_addr = CE_state->ctrl_addr; @@ -2166,6 +2230,8 @@ ce_send_cb_register(struct CE_handle *copyeng, void *ce_send_context, int disable_interrupts) { struct CE_state *CE_state = (struct CE_state *)copyeng; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); if (CE_state == NULL) { HIF_ERROR("%s: Error CE state = NULL", __func__); @@ -2173,7 +2239,8 @@ ce_send_cb_register(struct CE_handle *copyeng, } CE_state->send_context = ce_send_context; CE_state->send_cb = fn_ptr; - ce_per_engine_handler_adjust(CE_state, disable_interrupts); + hif_state->ce_services->ce_per_engine_handler_adjust(CE_state, + disable_interrupts); } /** @@ -2194,6 +2261,8 @@ ce_recv_cb_register(struct CE_handle *copyeng, void *CE_recv_context, int disable_interrupts) { struct CE_state *CE_state = (struct CE_state *)copyeng; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); if (CE_state == NULL) { HIF_ERROR("%s: ERROR CE state = NULL", __func__); @@ -2201,7 +2270,8 @@ ce_recv_cb_register(struct CE_handle *copyeng, } CE_state->recv_context = CE_recv_context; CE_state->recv_cb = fn_ptr; - ce_per_engine_handler_adjust(CE_state, disable_interrupts); + hif_state->ce_services->ce_per_engine_handler_adjust(CE_state, + disable_interrupts); } /** @@ -2217,10 +2287,13 @@ ce_watermark_cb_register(struct CE_handle *copyeng, CE_watermark_cb fn_ptr, void *CE_wm_context) { struct CE_state *CE_state = (struct CE_state *)copyeng; + struct hif_softc *scn = CE_state->scn; + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); CE_state->watermark_cb = fn_ptr; CE_state->wm_context = CE_wm_context; - ce_per_engine_handler_adjust(CE_state, 0); + hif_state->ce_services->ce_per_engine_handler_adjust(CE_state, + 0); if (fn_ptr) { CE_state->misc_cbs = 1; } @@ -2308,3 +2381,156 @@ void ce_ipa_get_resource(struct CE_handle *ce, } #endif /* IPA_OFFLOAD */ +bool ce_check_int_watermark(struct CE_state *CE_state, unsigned int *flags) +{ + uint32_t ce_int_status; + uint32_t ctrl_addr = CE_state->ctrl_addr; + struct hif_softc *scn = CE_state->scn; + + ce_int_status = CE_ENGINE_INT_STATUS_GET(scn, ctrl_addr); + if (ce_int_status & CE_WATERMARK_MASK) { + /* Convert HW IS bits to software flags */ + *flags = + (ce_int_status & CE_WATERMARK_MASK) >> + CE_WM_SHFT; + return true; + } + + return false; +} + +void ce_legacy_src_ring_setup(struct hif_softc *scn, uint32_t ce_id, + struct CE_ring_state *src_ring, + struct CE_attr *attr) +{ + uint32_t ctrl_addr; + uint64_t dma_addr; + + QDF_ASSERT(ce_id < scn->ce_count); + ctrl_addr = CE_BASE_ADDRESS(ce_id); + + src_ring->hw_index = + CE_SRC_RING_READ_IDX_GET_FROM_REGISTER(scn, ctrl_addr); + src_ring->sw_index = src_ring->hw_index; + src_ring->write_index = + CE_SRC_RING_WRITE_IDX_GET_FROM_REGISTER(scn, ctrl_addr); + dma_addr = src_ring->base_addr_CE_space; + CE_SRC_RING_BASE_ADDR_SET(scn, ctrl_addr, + (uint32_t)(dma_addr & 0xFFFFFFFF)); + + /* if SR_BA_ADDRESS_HIGH register exists */ + if (is_register_supported(SR_BA_ADDRESS_HIGH)) { + 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); + } + CE_SRC_RING_SZ_SET(scn, ctrl_addr, src_ring->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, src_ring->nentries); + +} + +void ce_legacy_dest_ring_setup(struct hif_softc *scn, uint32_t ce_id, + struct CE_ring_state *dest_ring, + struct CE_attr *attr) +{ + uint32_t ctrl_addr; + uint64_t dma_addr; + + QDF_ASSERT(ce_id < scn->ce_count); + ctrl_addr = CE_BASE_ADDRESS(ce_id); + dest_ring->sw_index = + CE_DEST_RING_READ_IDX_GET_FROM_REGISTER(scn, ctrl_addr); + dest_ring->write_index = + CE_DEST_RING_WRITE_IDX_GET_FROM_REGISTER(scn, ctrl_addr); + dma_addr = dest_ring->base_addr_CE_space; + CE_DEST_RING_BASE_ADDR_SET(scn, ctrl_addr, + (uint32_t)(dma_addr & 0xFFFFFFFF)); + + /* if DR_BA_ADDRESS_HIGH exists */ + if (is_register_supported(DR_BA_ADDRESS_HIGH)) { + 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); + } + + CE_DEST_RING_SZ_SET(scn, ctrl_addr, dest_ring->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, dest_ring->nentries); +} + +uint32_t ce_get_desc_size_legacy(uint8_t ring_type) +{ + switch (ring_type) { + case CE_RING_SRC: + return sizeof(struct CE_src_desc); + case CE_RING_DEST: + return sizeof(struct CE_dest_desc); + case CE_RING_STATUS: + qdf_assert(0); + return 0; + default: + return 0; + } + + return 0; +} + +void ce_ring_setup_legacy(struct hif_softc *scn, uint8_t ring_type, + uint32_t ce_id, struct CE_ring_state *ring, + struct CE_attr *attr) +{ + switch (ring_type) { + case CE_RING_SRC: + ce_legacy_src_ring_setup(scn, ce_id, ring, attr); + break; + case CE_RING_DEST: + ce_legacy_dest_ring_setup(scn, ce_id, ring, attr); + break; + case CE_RING_STATUS: + default: + qdf_assert(0); + break; + } +} + +struct ce_ops ce_service_legacy = { + .ce_get_desc_size = ce_get_desc_size_legacy, + .ce_ring_setup = ce_ring_setup_legacy, + .ce_sendlist_send = ce_sendlist_send_legacy, + .ce_completed_recv_next_nolock = ce_completed_recv_next_nolock_legacy, + .ce_revoke_recv_next = ce_revoke_recv_next_legacy, + .ce_cancel_send_next = ce_cancel_send_next_legacy, + .ce_recv_buf_enqueue = ce_recv_buf_enqueue_legacy, + .ce_per_engine_handler_adjust = ce_per_engine_handler_adjust_legacy, + .ce_send_nolock = ce_send_nolock_legacy, + .watermark_int = ce_check_int_watermark, + .ce_completed_send_next_nolock = ce_completed_send_next_nolock_legacy, + .ce_recv_entries_done_nolock = ce_recv_entries_done_nolock_legacy, + .ce_send_entries_done_nolock = ce_send_entries_done_nolock_legacy, +}; + + +struct ce_ops *ce_services_legacy() +{ + return &ce_service_legacy; +} diff --git a/hif/src/ce/ce_service_srng.c b/hif/src/ce/ce_service_srng.c new file mode 100644 index 000000000000..a1bd74fe34da --- /dev/null +++ b/hif/src/ce/ce_service_srng.c @@ -0,0 +1,689 @@ +/* + * Copyright (c) 2016 The Linux Foundation. All rights reserved. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted, provided that the + * above copyright notice and this permission notice appear in all + * copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE + * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "hif.h" +#include "hif_io32.h" +#include "reg_struct.h" +#include "ce_api.h" +#include "ce_main.h" +#include "ce_internal.h" +#include "ce_reg.h" +#include "qdf_lock.h" +#include "regtable.h" +#include "hif_main.h" +#include "hif_debug.h" +#include "hal_api.h" + +/* + * 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 + * receives 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 + */ + +#define CE_ADDR_COPY(desc, dma_addr) do {\ + (desc)->buffer_addr_lo = (uint32_t)((dma_addr) &\ + 0xFFFFFFFF);\ + (desc)->buffer_addr_hi =\ + (uint32_t)(((dma_addr) >> 32) & 0xFF);\ + } while (0) + +int +ce_send_nolock_srng(struct CE_handle *copyeng, + void *per_transfer_context, + qdf_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; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int write_index = src_ring->write_index; + uint64_t dma_addr = buffer; + struct hif_softc *scn = CE_state->scn; + + if (Q_TARGET_ACCESS_BEGIN(scn) < 0) + return QDF_STATUS_E_FAILURE; + if (unlikely(hal_srng_src_num_avail(scn->hal_soc, src_ring->srng_ctx, + false) <= 0)) { + OL_ATH_CE_PKT_ERROR_COUNT_INCR(scn, CE_RING_DELTA_FAIL); + Q_TARGET_ACCESS_END(scn); + return QDF_STATUS_E_FAILURE; + } + { + enum hif_ce_event_type event_type = HIF_TX_GATHER_DESC_POST; + struct ce_srng_src_desc *src_desc; + + if (hal_srng_access_start(scn->hal_soc, src_ring->srng_ctx)) { + Q_TARGET_ACCESS_END(scn); + return QDF_STATUS_E_FAILURE; + } + + src_desc = hal_srng_src_get_next_reaped(scn->hal_soc, + src_ring->srng_ctx); + + /* Update low 32 bits source descriptor address */ + src_desc->buffer_addr_lo = + (uint32_t)(dma_addr & 0xFFFFFFFF); + src_desc->buffer_addr_hi = + (uint32_t)((dma_addr >> 32) & 0xFF); + + 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) + */ + src_desc->byte_swap = + (((CE_state->attr_flags & CE_ATTR_BYTE_SWAP_DATA) + != 0) & ((flags & CE_SEND_FLAG_SWAP_DISABLE) == 0)); + src_desc->gather = ((flags & CE_SEND_FLAG_GATHER) != 0); + src_desc->nbytes = nbytes; + + src_ring->per_transfer_context[write_index] = + per_transfer_context; + write_index = CE_RING_IDX_INCR(nentries_mask, write_index); + + hal_srng_access_end(scn->hal_soc, src_ring->srng_ctx); + + /* src_ring->write index hasn't been updated event though + * the register has allready been written to. + */ + hif_record_ce_desc_event(scn, CE_state->id, event_type, + (union ce_desc *) src_desc, per_transfer_context, + src_ring->write_index); + + src_ring->write_index = write_index; + status = QDF_STATUS_SUCCESS; + } + Q_TARGET_ACCESS_END(scn); + return status; +} + +int +ce_sendlist_send_srng(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 num_items = sl->num_items; + unsigned int sw_index; + unsigned int write_index; + struct hif_softc *scn = CE_state->scn; + + QDF_ASSERT((num_items > 0) && (num_items < src_ring->nentries)); + + qdf_spin_lock_bh(&CE_state->ce_index_lock); + sw_index = src_ring->sw_index; + write_index = src_ring->write_index; + + if (hal_srng_src_num_avail(scn->hal_soc, src_ring->srng_ctx, false) >= + 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? */ + QDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE); + status = ce_send_nolock_srng(copyeng, + CE_SENDLIST_ITEM_CTXT, + (qdf_dma_addr_t) item->data, + item->u.nbytes, transfer_id, + item->flags | CE_SEND_FLAG_GATHER, + item->user_flags); + QDF_ASSERT(status == QDF_STATUS_SUCCESS); + } + /* provide valid context pointer for final item */ + item = &sl->item[i]; + /* TBDXXX: Support extensible sendlist_types? */ + QDF_ASSERT(item->send_type == CE_SIMPLE_BUFFER_TYPE); + status = ce_send_nolock_srng(copyeng, per_transfer_context, + (qdf_dma_addr_t) item->data, + item->u.nbytes, + transfer_id, item->flags, + item->user_flags); + QDF_ASSERT(status == QDF_STATUS_SUCCESS); + QDF_NBUF_UPDATE_TX_PKT_COUNT((qdf_nbuf_t)per_transfer_context, + QDF_NBUF_TX_PKT_CE); + DPTRACE(qdf_dp_trace((qdf_nbuf_t)per_transfer_context, + QDF_DP_TRACE_CE_PACKET_PTR_RECORD, + (uint8_t *)(((qdf_nbuf_t)per_transfer_context)->data), + sizeof(((qdf_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. + */ + } + qdf_spin_unlock_bh(&CE_state->ce_index_lock); + + return status; +} + +#define SLOTS_PER_DATAPATH_TX 2 + +#ifndef AH_NEED_TX_DATA_SWAP +#define AH_NEED_TX_DATA_SWAP 0 +#endif +/** + * ce_recv_buf_enqueue_srng() - enqueue a recv buffer into a copy engine + * @coyeng: copy engine handle + * @per_recv_context: virtual address of the nbuf + * @buffer: physical address of the nbuf + * + * Return: 0 if the buffer is enqueued + */ +int +ce_recv_buf_enqueue_srng(struct CE_handle *copyeng, + void *per_recv_context, qdf_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; + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int write_index; + unsigned int sw_index; + uint64_t dma_addr = buffer; + struct hif_softc *scn = CE_state->scn; + + qdf_spin_lock_bh(&CE_state->ce_index_lock); + write_index = dest_ring->write_index; + sw_index = dest_ring->sw_index; + + if (Q_TARGET_ACCESS_BEGIN(scn) < 0) { + qdf_spin_unlock_bh(&CE_state->ce_index_lock); + return -EIO; + } + + if (hal_srng_access_start(scn->hal_soc, dest_ring->srng_ctx)) { + qdf_spin_unlock_bh(&CE_state->ce_index_lock); + return QDF_STATUS_E_FAILURE; + } + + if ((hal_srng_src_num_avail(scn->hal_soc, + dest_ring->srng_ctx, false) > 0)) { + struct ce_srng_dest_desc *dest_desc = + hal_srng_src_get_next(scn->hal_soc, + dest_ring->srng_ctx); + + if (dest_desc == NULL) { + status = QDF_STATUS_E_FAILURE; + } else { + + CE_ADDR_COPY(dest_desc, dma_addr); + + 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); + status = QDF_STATUS_SUCCESS; + } + } else + status = QDF_STATUS_E_FAILURE; + + dest_ring->write_index = write_index; + hal_srng_access_end(scn->hal_soc, dest_ring->srng_ctx); + Q_TARGET_ACCESS_END(scn); + qdf_spin_unlock_bh(&CE_state->ce_index_lock); + return status; +} + +/** + * ce_send_watermarks_set_srng + */ +void +ce_send_watermarks_set_srng(struct CE_handle *copyeng, + unsigned int low_alert_nentries, + unsigned int high_alert_nentries) +{ + /*TODO*/ +} +/* + * ce_recv_watermarks_set_srng + */ +void +ce_recv_watermarks_set_srng(struct CE_handle *copyeng, + unsigned int low_alert_nentries, + unsigned int high_alert_nentries) +{ + /*TODO*/ +} + +unsigned int ce_send_entries_avail_srng(struct CE_handle *copyeng) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct CE_ring_state *src_ring = CE_state->src_ring; + struct hif_softc *scn = CE_state->scn; + + return hal_srng_src_num_avail(scn->hal_soc, src_ring->srng_ctx, false); +} + +unsigned int ce_recv_entries_avail_srng(struct CE_handle *copyeng) +{ + struct CE_state *CE_state = (struct CE_state *)copyeng; + struct CE_ring_state *dest_ring = CE_state->dest_ring; + struct hif_softc *scn = CE_state->scn; + + + return hal_srng_src_num_avail(scn->hal_soc, dest_ring->srng_ctx, false); +} + +/* + * Guts of ce_recv_entries_done. + * The caller takes responsibility for any necessary locking. + */ +unsigned int +ce_recv_entries_done_nolock_srng(struct hif_softc *scn, + struct CE_state *CE_state) +{ + struct CE_ring_state *status_ring = CE_state->status_ring; + + return hal_srng_dst_num_valid(scn->hal_soc, + status_ring->srng_ctx, false); +} + +/* + * Guts of ce_send_entries_done. + * The caller takes responsibility for any necessary locking. + */ +unsigned int +ce_send_entries_done_nolock_srng(struct hif_softc *scn, + struct CE_state *CE_state) +{ + + struct CE_ring_state *src_ring = CE_state->src_ring; + int count = 0; + + if (hal_srng_access_start(scn->hal_soc, src_ring->srng_ctx)) + return 0; + + count = hal_srng_src_done_val(scn->hal_soc, src_ring->srng_ctx); + + hal_srng_access_end(scn->hal_soc, src_ring->srng_ctx); + + return count; +} + +/* Debug support */ +void *ce_debug_cmplrn_context_srng; /* completed recv next context */ +void *ce_debug_cmplsn_context_srng; /* completed send next context */ + +/* + * Guts of ce_completed_recv_next. + * The caller takes responsibility for any necessary locking. + */ +int +ce_completed_recv_next_nolock_srng(struct CE_state *CE_state, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_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; + struct CE_ring_state *status_ring = CE_state->status_ring; + unsigned int nentries_mask = dest_ring->nentries_mask; + unsigned int sw_index = dest_ring->sw_index; + struct hif_softc *scn = CE_state->scn; + struct ce_srng_dest_status_desc *dest_status; + int nbytes; + struct ce_srng_dest_status_desc dest_status_info; + + if (hal_srng_access_start(scn->hal_soc, status_ring->srng_ctx)) { + status = QDF_STATUS_E_FAILURE; + goto done; + } + + dest_status = hal_srng_dst_get_next(scn->hal_soc, + status_ring->srng_ctx); + + if (dest_status == NULL) { + status = QDF_STATUS_E_FAILURE; + goto done; + } + /* + * By copying the dest_desc_info element to local memory, we could + * avoid extra memory read from non-cachable memory. + */ + dest_status_info = *dest_status; + nbytes = dest_status_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 = QDF_STATUS_E_FAILURE; + goto done; + } + + dest_status->nbytes = 0; + + *nbytesp = nbytes; + *transfer_idp = dest_status_info.meta_data; + *flagsp = (dest_status_info.byte_swap) ? CE_RECV_FLAG_SWAPPED : 0; + + if (per_CE_contextp) + *per_CE_contextp = CE_state->recv_context; + + /* NOTE: sw_index is more like a read_index in this context. It has a + * one-to-one mapping with status ring. + * Get the per trasnfer context from dest_ring. + */ + ce_debug_cmplrn_context_srng = + dest_ring->per_transfer_context[sw_index]; + + if (per_transfer_contextp) + *per_transfer_contextp = ce_debug_cmplrn_context_srng; + + 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 = QDF_STATUS_SUCCESS; + +done: + hal_srng_access_end(scn->hal_soc, status_ring->srng_ctx); + + return status; +} + +QDF_STATUS +ce_revoke_recv_next_srng(struct CE_handle *copyeng, + void **per_CE_contextp, + void **per_transfer_contextp, qdf_dma_addr_t *bufferp) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + return status; +} + +/* + * Guts of ce_completed_send_next. + * The caller takes responsibility for any necessary locking. + */ +int +ce_completed_send_next_nolock_srng(struct CE_state *CE_state, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_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 = QDF_STATUS_E_FAILURE; + struct CE_ring_state *src_ring = CE_state->src_ring; + unsigned int nentries_mask = src_ring->nentries_mask; + unsigned int sw_index = src_ring->sw_index; + struct hif_softc *scn = CE_state->scn; + struct ce_srng_src_desc *src_desc; + + if (hal_srng_access_start(scn->hal_soc, src_ring->srng_ctx)) { + status = QDF_STATUS_E_FAILURE; + return status; + } + + src_desc = hal_srng_src_reap_next(scn->hal_soc, src_ring->srng_ctx); + if (src_desc) { + + /* Return data from completed source descriptor */ + *bufferp = (qdf_dma_addr_t) + (((uint64_t)(src_desc)->buffer_addr_lo + + ((uint64_t)((src_desc)->buffer_addr_hi & + 0xFF) << 32))); + *nbytesp = src_desc->nbytes; + *transfer_idp = src_desc->meta_data; + *toeplitz_hash_result = 0; /*src_desc->toeplitz_hash_result;*/ + + if (per_CE_contextp) + *per_CE_contextp = CE_state->send_context; + + /* sw_index is used more like read index */ + ce_debug_cmplsn_context_srng = + src_ring->per_transfer_context[sw_index]; + if (per_transfer_contextp) + *per_transfer_contextp = ce_debug_cmplsn_context_srng; + + 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 = QDF_STATUS_SUCCESS; + } + hal_srng_access_end(scn->hal_soc, src_ring->srng_ctx); + + return status; +} + +/* NB: Modelled after ce_completed_send_next */ +QDF_STATUS +ce_cancel_send_next_srng(struct CE_handle *copyeng, + void **per_CE_contextp, + void **per_transfer_contextp, + qdf_dma_addr_t *bufferp, + unsigned int *nbytesp, + unsigned int *transfer_idp, + uint32_t *toeplitz_hash_result) +{ + return 0; +} + +/* Shift bits to convert IS_*_RING_*_WATERMARK_MASK to CE_WM_FLAG_*_* */ +#define CE_WM_SHFT 1 + +/* + * 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 + +/* + * 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_srng(struct CE_state *CE_state, + int disable_copy_compl_intr) +{ +} + +bool ce_check_int_watermark_srng(struct CE_state *CE_state, unsigned int *flags) +{ + /*TODO*/ + return false; +} + +uint32_t ce_get_desc_size_srng(uint8_t ring_type) +{ + switch (ring_type) { + case CE_RING_SRC: + return sizeof(struct ce_srng_src_desc); + case CE_RING_DEST: + return sizeof(struct ce_srng_dest_desc); + case CE_RING_STATUS: + return sizeof(struct ce_srng_dest_status_desc); + default: + return 0; + } + return 0; +} + +void ce_srng_src_ring_setup(struct hif_softc *scn, uint32_t ce_id, + struct CE_ring_state *src_ring) +{ + struct hal_srng_params ring_params = {0}; + + ring_params.ring_base_paddr = src_ring->base_addr_CE_space; + ring_params.ring_base_vaddr = src_ring->base_addr_owner_space; + ring_params.num_entries = src_ring->nentries; + ring_params.intr_timer_thres_us = 4; + ring_params.intr_batch_cntr_thres_entries = 1; + + /* TODO + * ring_params.msi_addr = XXX; + * ring_params.msi_data = XXX; + * ring_params.flags = XXX; + */ + + src_ring->srng_ctx = hal_srng_setup(scn->hal_soc, CE_SRC, ce_id, 0, + &ring_params); +} + +void ce_srng_dest_ring_setup(struct hif_softc *scn, uint32_t ce_id, + struct CE_ring_state *dest_ring) +{ + struct hal_srng_params ring_params = {0}; + + ring_params.ring_base_paddr = dest_ring->base_addr_CE_space; + ring_params.ring_base_vaddr = dest_ring->base_addr_owner_space; + ring_params.num_entries = dest_ring->nentries; + ring_params.intr_timer_thres_us = 4; + ring_params.intr_batch_cntr_thres_entries = 1; + + /* TODO + * ring_params.msi_addr = XXX; + * ring_params.msi_data = XXX; + * ring_params.flags = XXX; + */ + + /*Dest ring is also source ring*/ + dest_ring->srng_ctx = hal_srng_setup(scn->hal_soc, CE_DST, ce_id, 0, + &ring_params); +} + +void ce_srng_status_ring_setup(struct hif_softc *scn, uint32_t ce_id, + struct CE_ring_state *status_ring) +{ + struct hal_srng_params ring_params = {0}; + + ring_params.ring_base_paddr = status_ring->base_addr_CE_space; + ring_params.ring_base_vaddr = status_ring->base_addr_owner_space; + ring_params.num_entries = status_ring->nentries; + ring_params.intr_timer_thres_us = 4; + ring_params.intr_batch_cntr_thres_entries = 1; + + /* TODO + * ring_params.msi_addr = XXX; + * ring_params.msi_data = XXX; + * ring_params.flags = XXX; + */ + + status_ring->srng_ctx = hal_srng_setup(scn->hal_soc, CE_DST_STATUS, + ce_id, 0, &ring_params); +} + +void ce_ring_setup_srng(struct hif_softc *scn, uint8_t ring_type, + uint32_t ce_id, struct CE_ring_state *ring, + struct CE_attr *attr) +{ + switch (ring_type) { + case CE_RING_SRC: + ce_srng_src_ring_setup(scn, ce_id, ring); + break; + case CE_RING_DEST: + ce_srng_dest_ring_setup(scn, ce_id, ring); + break; + case CE_RING_STATUS: + ce_srng_status_ring_setup(scn, ce_id, ring); + break; + default: + qdf_assert(0); + break; + } +} +struct ce_ops ce_service_srng = { + .ce_get_desc_size = ce_get_desc_size_srng, + .ce_ring_setup = ce_ring_setup_srng, + .ce_sendlist_send = ce_sendlist_send_srng, + .ce_completed_recv_next_nolock = ce_completed_recv_next_nolock_srng, + .ce_revoke_recv_next = ce_revoke_recv_next_srng, + .ce_cancel_send_next = ce_cancel_send_next_srng, + .ce_recv_buf_enqueue = ce_recv_buf_enqueue_srng, + .ce_per_engine_handler_adjust = ce_per_engine_handler_adjust_srng, + .ce_send_nolock = ce_send_nolock_srng, + .watermark_int = ce_check_int_watermark_srng, + .ce_completed_send_next_nolock = ce_completed_send_next_nolock_srng, + .ce_recv_entries_done_nolock = ce_recv_entries_done_nolock_srng, + .ce_send_entries_done_nolock = ce_send_entries_done_nolock_srng, +}; + +struct ce_ops *ce_services_srng() +{ + return &ce_service_srng; +} diff --git a/hif/src/hif_main.c b/hif/src/hif_main.c index 936a45127e4c..d5daaf4f9ee7 100644 --- a/hif/src/hif_main.c +++ b/hif/src/hif_main.c @@ -46,6 +46,9 @@ #include "hif_debug.h" #include "mp_dev.h" #include "ce_api.h" +#ifdef QCA_WIFI_QCA8074 +#include "hal_api.h" +#endif void hif_dump(struct hif_opaque_softc *hif_ctx, uint8_t cmd_id, bool start) { @@ -339,6 +342,20 @@ void hif_get_hw_info(struct hif_opaque_softc *scn, u32 *version, u32 *revision, *target_name = hif_get_hw_name(info); } +/** + * hif_get_dev_ba(): API to get device base address. + * @scn: scn + * @version: version + * @revision: revision + * + * Return: n/a + */ +void *hif_get_dev_ba(struct hif_opaque_softc *hif_handle) +{ + struct hif_softc *scn = (struct hif_softc *)hif_handle; + + return scn->mem; +} /** * hif_open(): hif_open * @qdf_ctx: QDF Context @@ -415,6 +432,18 @@ void hif_close(struct hif_opaque_softc *hif_ctx) qdf_mem_free(scn); } +static QDF_STATUS hif_hal_attach(struct hif_softc *scn) +{ +#ifdef QCA_WIFI_QCA8074 + if (ce_srng_based(scn)) { + scn->hal_soc = hal_attach(scn, scn->qdf_dev); + if (scn->hal_soc == NULL) + return QDF_STATUS_E_FAILURE; + } +#endif + + return QDF_STATUS_SUCCESS; +} /** * hif_enable(): hif_enable * @hif_ctx: hif_ctx @@ -446,6 +475,12 @@ QDF_STATUS hif_enable(struct hif_opaque_softc *hif_ctx, struct device *dev, return status; } + status = hif_hal_attach(scn); + if (status != QDF_STATUS_SUCCESS) { + HIF_ERROR("%s: hal attach failed", __func__); + return status; + } + if (hif_bus_configure(scn)) { HIF_ERROR("%s: Target probe failed.", __func__); hif_disable_bus(scn); @@ -914,6 +949,7 @@ qdf_nbuf_t hif_batch_send(struct hif_opaque_softc *osc, qdf_nbuf_t msdu, uint32_t transfer_id, u_int32_t len, uint32_t sendhead) { void *ce_tx_hdl = hif_get_ce_handle(osc, CE_HTT_TX_CE); + return ce_batch_send((struct CE_handle *)ce_tx_hdl, msdu, transfer_id, len, sendhead); } @@ -947,6 +983,7 @@ int hif_send_single(struct hif_opaque_softc *osc, qdf_nbuf_t msdu, uint32_t transfer_id, u_int32_t len) { void *ce_tx_hdl = hif_get_ce_handle(osc, CE_HTT_TX_CE); + return ce_send_single((struct CE_handle *)ce_tx_hdl, msdu, transfer_id, len); } @@ -966,6 +1003,7 @@ int hif_send_fast(struct hif_opaque_softc *osc, qdf_nbuf_t nbuf, uint32_t transfer_id, uint32_t download_len) { void *ce_tx_hdl = hif_get_ce_handle(osc, CE_HTT_TX_CE); + return ce_send_fast((struct CE_handle *)ce_tx_hdl, nbuf, transfer_id, download_len); } diff --git a/hif/src/hif_main.h b/hif/src/hif_main.h index 0d0ae165c69d..62bcdd041bee 100644 --- a/hif/src/hif_main.h +++ b/hif/src/hif_main.h @@ -155,6 +155,7 @@ struct hif_softc { #ifdef QCA_NSS_WIFI_OFFLOAD_SUPPORT uint32_t nss_wifi_ol_mode; #endif + void *hal_soc; }; #ifdef QCA_NSS_WIFI_OFFLOAD_SUPPORT