| /* |
| * Copyright (c) 2011, 2014-2017 The Linux Foundation. All rights reserved. |
| * |
| * Previously licensed under the ISC license by Qualcomm Atheros, Inc. |
| * |
| * |
| * Permission to use, copy, modify, and/or distribute this software for |
| * any purpose with or without fee is hereby granted, provided that the |
| * above copyright notice and this permission notice appear in all |
| * copies. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL |
| * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE |
| * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL |
| * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR |
| * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER |
| * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR |
| * PERFORMANCE OF THIS SOFTWARE. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /** |
| * @file htt.c |
| * @brief Provide functions to create+init and destroy a HTT instance. |
| * @details |
| * This file contains functions for creating a HTT instance; initializing |
| * the HTT instance, e.g. by allocating a pool of HTT tx descriptors and |
| * connecting the HTT service with HTC; and deleting a HTT instance. |
| */ |
| |
| #include <adf_os_mem.h> /* adf_os_mem_alloc */ |
| #include <adf_os_types.h> /* adf_os_device_t, adf_os_print */ |
| |
| #include <htt.h> /* htt_tx_msdu_desc_t */ |
| #include <ol_cfg.h> |
| #include <ol_txrx_htt_api.h> /* ol_tx_dowload_done_ll, etc. */ |
| #include <ol_htt_api.h> |
| |
| #include <htt_internal.h> |
| #if defined(HIF_PCI) |
| #include "if_pci.h" |
| #endif |
| |
| #define HTT_HTC_PKT_POOL_INIT_SIZE 100 /* enough for a large A-MPDU */ |
| |
| A_STATUS |
| htt_h2t_rx_ring_cfg_msg_ll(struct htt_pdev_t *pdev); |
| |
| A_STATUS |
| htt_h2t_rx_ring_cfg_msg_hl(struct htt_pdev_t *pdev); |
| |
| A_STATUS (*htt_h2t_rx_ring_cfg_msg)( |
| struct htt_pdev_t *pdev); |
| |
| #ifdef IPA_UC_OFFLOAD |
| A_STATUS |
| htt_ipa_config(htt_pdev_handle pdev, A_STATUS status) |
| { |
| if ((A_OK == status) && |
| ol_cfg_ipa_uc_offload_enabled(pdev->ctrl_pdev)) { |
| status = htt_h2t_ipa_uc_rsc_cfg_msg(pdev); |
| } |
| return status; |
| } |
| |
| #define HTT_IPA_CONFIG htt_ipa_config |
| #else |
| #define HTT_IPA_CONFIG(pdev, status) status /* no-op */ |
| #endif /* IPA_UC_OFFLOAD */ |
| |
| |
| struct htt_htc_pkt * |
| htt_htc_pkt_alloc(struct htt_pdev_t *pdev) |
| { |
| struct htt_htc_pkt_union *pkt = NULL; |
| |
| HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); |
| if (pdev->htt_htc_pkt_freelist) { |
| pkt = pdev->htt_htc_pkt_freelist; |
| pdev->htt_htc_pkt_freelist = pdev->htt_htc_pkt_freelist->u.next; |
| } |
| HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); |
| |
| if (pkt == NULL) { |
| pkt = adf_os_mem_alloc(pdev->osdev, sizeof(*pkt)); |
| } |
| return &pkt->u.pkt; /* not actually a dereference */ |
| } |
| |
| void |
| htt_htc_pkt_free(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt) |
| { |
| struct htt_htc_pkt_union *u_pkt = (struct htt_htc_pkt_union *) pkt; |
| |
| HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); |
| u_pkt->u.next = pdev->htt_htc_pkt_freelist; |
| pdev->htt_htc_pkt_freelist = u_pkt; |
| HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); |
| } |
| |
| void |
| htt_htc_pkt_pool_free(struct htt_pdev_t *pdev) |
| { |
| struct htt_htc_pkt_union *pkt, *next; |
| pkt = pdev->htt_htc_pkt_freelist; |
| while (pkt) { |
| next = pkt->u.next; |
| adf_os_mem_free(pkt); |
| pkt = next; |
| } |
| pdev->htt_htc_pkt_freelist = NULL; |
| } |
| |
| #ifdef ATH_11AC_TXCOMPACT |
| void |
| htt_htc_misc_pkt_list_trim(struct htt_pdev_t *pdev, int level) |
| { |
| struct htt_htc_pkt_union *pkt, *next, *prev = NULL; |
| int i = 0; |
| adf_nbuf_t netbuf; |
| |
| HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); |
| pkt = pdev->htt_htc_pkt_misclist; |
| while (pkt) { |
| next = pkt->u.next; |
| /* trim the out grown list*/ |
| if (++i > level) { |
| netbuf = (adf_nbuf_t)(pkt->u.pkt.htc_pkt.pNetBufContext); |
| adf_nbuf_unmap(pdev->osdev, netbuf, ADF_OS_DMA_TO_DEVICE); |
| adf_nbuf_free(netbuf); |
| adf_os_mem_free(pkt); |
| pkt = NULL; |
| if (prev) |
| prev->u.next = NULL; |
| } |
| prev = pkt; |
| pkt = next; |
| } |
| HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); |
| } |
| |
| void |
| htt_htc_misc_pkt_list_add(struct htt_pdev_t *pdev, struct htt_htc_pkt *pkt) |
| { |
| struct htt_htc_pkt_union *u_pkt = (struct htt_htc_pkt_union *) pkt; |
| |
| HTT_TX_MUTEX_ACQUIRE(&pdev->htt_tx_mutex); |
| if (pdev->htt_htc_pkt_misclist) { |
| u_pkt->u.next = pdev->htt_htc_pkt_misclist; |
| pdev->htt_htc_pkt_misclist = u_pkt; |
| } else { |
| pdev->htt_htc_pkt_misclist = u_pkt; |
| } |
| HTT_TX_MUTEX_RELEASE(&pdev->htt_tx_mutex); |
| htt_htc_misc_pkt_list_trim(pdev, HTT_HTC_PKT_MISCLIST_SIZE); |
| } |
| |
| void |
| htt_htc_misc_pkt_pool_free(struct htt_pdev_t *pdev) |
| { |
| struct htt_htc_pkt_union *pkt, *next; |
| adf_nbuf_t netbuf; |
| pkt = pdev->htt_htc_pkt_misclist; |
| |
| while (pkt) { |
| next = pkt->u.next; |
| netbuf = (adf_nbuf_t)(pkt->u.pkt.htc_pkt.pNetBufContext); |
| adf_nbuf_unmap(pdev->osdev, netbuf, ADF_OS_DMA_TO_DEVICE); |
| adf_nbuf_free(netbuf); |
| adf_os_mem_free(pkt); |
| pkt = next; |
| } |
| pdev->htt_htc_pkt_misclist = NULL; |
| } |
| #endif |
| |
| /*---*/ |
| |
| htt_pdev_handle |
| htt_attach( |
| ol_txrx_pdev_handle txrx_pdev, |
| ol_pdev_handle ctrl_pdev, |
| HTC_HANDLE htc_pdev, |
| adf_os_device_t osdev, |
| int desc_pool_size) |
| { |
| struct htt_pdev_t *pdev; |
| int i; |
| |
| pdev = adf_os_mem_alloc(osdev, sizeof(*pdev)); |
| |
| if (!pdev) { |
| goto fail1; |
| } |
| |
| pdev->osdev = osdev; |
| pdev->ctrl_pdev = ctrl_pdev; |
| pdev->txrx_pdev = txrx_pdev; |
| pdev->htc_pdev = htc_pdev; |
| |
| adf_os_mem_set(&pdev->stats, 0, sizeof(pdev->stats)); |
| pdev->htt_htc_pkt_freelist = NULL; |
| #ifdef ATH_11AC_TXCOMPACT |
| pdev->htt_htc_pkt_misclist = NULL; |
| #endif |
| |
| /* for efficiency, store a local copy of the is_high_latency flag */ |
| pdev->cfg.is_high_latency = ol_cfg_is_high_latency(pdev->ctrl_pdev); |
| pdev->cfg.default_tx_comp_req = |
| !ol_cfg_tx_free_at_download(pdev->ctrl_pdev); |
| |
| pdev->cfg.is_full_reorder_offload = |
| ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev); |
| adf_os_print("is_full_reorder_offloaded? %d\n", |
| (int)pdev->cfg.is_full_reorder_offload); |
| pdev->targetdef = htc_get_targetdef(htc_pdev); |
| /* |
| * Connect to HTC service. |
| * This has to be done before calling htt_rx_attach, |
| * since htt_rx_attach involves sending a rx ring configure |
| * message to the target. |
| */ |
| //AR6004 don't need HTT layer. |
| #ifndef AR6004_HW |
| if (htt_htc_attach(pdev)) { |
| goto fail2; |
| } |
| #endif |
| if (htt_tx_attach(pdev, desc_pool_size)) { |
| goto fail2; |
| } |
| |
| if (htt_rx_attach(pdev)) { |
| goto fail3; |
| } |
| |
| HTT_TX_MUTEX_INIT(&pdev->htt_tx_mutex); |
| HTT_TX_NBUF_QUEUE_MUTEX_INIT(pdev); |
| HTT_TX_MUTEX_INIT(&pdev->credit_mutex); |
| |
| /* pre-allocate some HTC_PACKET objects */ |
| for (i = 0; i < HTT_HTC_PKT_POOL_INIT_SIZE; i++) { |
| struct htt_htc_pkt_union *pkt; |
| pkt = adf_os_mem_alloc(pdev->osdev, sizeof(*pkt)); |
| if (! pkt) { |
| break; |
| } |
| htt_htc_pkt_free(pdev, &pkt->u.pkt); |
| } |
| |
| if (pdev->cfg.is_high_latency) { |
| /* |
| * HL - download the whole frame. |
| * Specify a download length greater than the max MSDU size, |
| * so the downloads will be limited by the actual frame sizes. |
| */ |
| pdev->download_len = 5000; |
| if (ol_cfg_tx_free_at_download(pdev->ctrl_pdev)) { |
| pdev->tx_send_complete_part2 = ol_tx_download_done_hl_free; |
| } else { |
| pdev->tx_send_complete_part2 = ol_tx_download_done_hl_retain; |
| } |
| |
| /* |
| * For LL, the FW rx desc directly referenced at its location |
| * inside the rx indication message. |
| */ |
| /* |
| * CHECK THIS LATER: does the HL HTT version of htt_rx_mpdu_desc_list_next |
| * (which is not currently implemented) present the adf_nbuf_data(rx_ind_msg) |
| * as the abstract rx descriptor? |
| * If not, the rx_fw_desc_offset initialization here will have to be |
| * adjusted accordingly. |
| * NOTE: for HL, because fw rx desc is in ind msg, not in rx desc, so the |
| * offset should be negtive value |
| */ |
| pdev->rx_fw_desc_offset = |
| HTT_ENDIAN_BYTE_IDX_SWAP( |
| HTT_RX_IND_FW_RX_DESC_BYTE_OFFSET |
| - HTT_RX_IND_HL_BYTES); |
| |
| htt_h2t_rx_ring_cfg_msg = htt_h2t_rx_ring_cfg_msg_hl; |
| |
| /* initialize the txrx credit count */ |
| ol_tx_target_credit_update( |
| pdev->txrx_pdev, ol_cfg_target_tx_credit(ctrl_pdev)); |
| } else { |
| /* |
| * LL - download just the initial portion of the frame. |
| * Download enough to cover the encapsulation headers checked |
| * by the target's tx classification descriptor engine. |
| */ |
| enum wlan_frm_fmt frm_type; |
| |
| /* account for the 802.3 or 802.11 header */ |
| frm_type = ol_cfg_frame_type(pdev->ctrl_pdev); |
| if (frm_type == wlan_frm_fmt_native_wifi) { |
| pdev->download_len = HTT_TX_HDR_SIZE_NATIVE_WIFI; |
| } else if (frm_type == wlan_frm_fmt_802_3) { |
| pdev->download_len = HTT_TX_HDR_SIZE_ETHERNET; |
| } else { |
| adf_os_print("Unexpected frame type spec: %d\n", frm_type); |
| HTT_ASSERT0(0); |
| } |
| /* |
| * Account for the optional L2 / ethernet header fields: |
| * 802.1Q, LLC/SNAP |
| */ |
| pdev->download_len += |
| HTT_TX_HDR_SIZE_802_1Q + HTT_TX_HDR_SIZE_LLC_SNAP; |
| |
| /* |
| * Account for the portion of the L3 (IP) payload that the |
| * target needs for its tx classification. |
| */ |
| pdev->download_len += ol_cfg_tx_download_size(pdev->ctrl_pdev); |
| |
| /* |
| * Account for the HTT tx descriptor, including the |
| * HTC header + alignment padding. |
| */ |
| pdev->download_len += sizeof(struct htt_host_tx_desc_t); |
| |
| /* |
| * The TXCOMPACT htt_tx_sched function uses pdev->download_len |
| * to apply for all requeued tx frames. Thus, pdev->download_len |
| * has to be the largest download length of any tx frame that will |
| * be downloaded. |
| * This maximum download length is for management tx frames, |
| * which have an 802.11 header. |
| */ |
| #ifdef ATH_11AC_TXCOMPACT |
| pdev->download_len = |
| sizeof(struct htt_host_tx_desc_t) + |
| HTT_TX_HDR_SIZE_OUTER_HDR_MAX + /* worst case */ |
| HTT_TX_HDR_SIZE_802_1Q + |
| HTT_TX_HDR_SIZE_LLC_SNAP + |
| ol_cfg_tx_download_size(pdev->ctrl_pdev); |
| #endif |
| pdev->tx_send_complete_part2 = ol_tx_download_done_ll; |
| |
| /* |
| * For LL, the FW rx desc is alongside the HW rx desc fields in |
| * the htt_host_rx_desc_base struct/. |
| */ |
| pdev->rx_fw_desc_offset = RX_STD_DESC_FW_MSDU_OFFSET; |
| |
| htt_h2t_rx_ring_cfg_msg = htt_h2t_rx_ring_cfg_msg_ll; |
| } |
| |
| return pdev; |
| |
| fail3: |
| htt_tx_detach(pdev); |
| |
| fail2: |
| adf_os_mem_free(pdev); |
| |
| fail1: |
| return NULL; |
| } |
| |
| A_STATUS |
| htt_attach_target(htt_pdev_handle pdev) |
| { |
| A_STATUS status; |
| status = htt_h2t_ver_req_msg(pdev); |
| if (status != A_OK) { |
| return status; |
| } |
| /* |
| * If applicable, send the rx ring config message to the target. |
| * The host could wait for the HTT version number confirmation message |
| * from the target before sending any further HTT messages, but it's |
| * reasonable to assume that the host and target HTT version numbers |
| * match, and proceed immediately with the remaining configuration |
| * handshaking. |
| */ |
| |
| status = htt_h2t_rx_ring_cfg_msg(pdev); |
| status = HTT_IPA_CONFIG(pdev, status); |
| |
| return status; |
| } |
| |
| void htt_htc_detach(struct htt_pdev_t *pdev) |
| { |
| htc_disconnect_service(pdev->htc_endpoint); |
| return; |
| } |
| |
| |
| void |
| htt_detach(htt_pdev_handle pdev) |
| { |
| htt_htc_detach(pdev); |
| htt_rx_detach(pdev); |
| htt_tx_detach(pdev); |
| htt_htc_pkt_pool_free(pdev); |
| #ifdef ATH_11AC_TXCOMPACT |
| htt_htc_misc_pkt_pool_free(pdev); |
| #endif |
| HTT_TX_MUTEX_DESTROY(&pdev->htt_tx_mutex); |
| HTT_TX_NBUF_QUEUE_MUTEX_DESTROY(pdev); |
| HTT_TX_MUTEX_DESTROY(&pdev->credit_mutex); |
| #ifdef DEBUG_RX_RING_BUFFER |
| if (pdev->rx_buff_list) |
| adf_os_mem_free(pdev->rx_buff_list); |
| #endif |
| adf_os_mem_free(pdev); |
| } |
| |
| void |
| htt_detach_target(htt_pdev_handle pdev) |
| { |
| } |
| |
| int |
| htt_htc_attach(struct htt_pdev_t *pdev) |
| { |
| HTC_SERVICE_CONNECT_REQ connect; |
| HTC_SERVICE_CONNECT_RESP response; |
| A_STATUS status; |
| |
| adf_os_mem_set(&connect, 0, sizeof(connect)); |
| adf_os_mem_set(&response, 0, sizeof(response)); |
| |
| connect.pMetaData = NULL; |
| connect.MetaDataLength = 0; |
| connect.EpCallbacks.pContext = pdev; |
| connect.EpCallbacks.EpTxComplete = htt_h2t_send_complete; |
| connect.EpCallbacks.EpTxCompleteMultiple = NULL; |
| connect.EpCallbacks.EpRecv = htt_t2h_msg_handler; |
| connect.EpCallbacks.EpResumeTxQueue = htt_tx_resume_handler; |
| |
| /* rx buffers currently are provided by HIF, not by EpRecvRefill */ |
| connect.EpCallbacks.EpRecvRefill = NULL; |
| connect.EpCallbacks.RecvRefillWaterMark = 1; /* N/A, fill is done by HIF */ |
| |
| connect.EpCallbacks.EpSendFull = htt_h2t_full; |
| /* |
| * Specify how deep to let a queue get before HTCSendPkt will |
| * call the EpSendFull function due to excessive send queue depth. |
| */ |
| connect.MaxSendQueueDepth = HTT_MAX_SEND_QUEUE_DEPTH; |
| |
| /* disable flow control for HTT data message service */ |
| #ifdef HIF_SDIO |
| /* |
| * HTC Credit mechanism is disabled based on |
| * default_tx_comp_req as throughput will be lower |
| * if we disable htc credit mechanism with default_tx_comp_req |
| * set since txrx download packet will be limited by ota |
| * completion. |
| * TODO:Conditional disabling will be removed once firmware |
| * with reduced tx completion is pushed into release builds. |
| */ |
| if ((!pdev->cfg.default_tx_comp_req) || |
| ol_cfg_is_ptp_enabled(pdev->ctrl_pdev)) { |
| connect.ConnectionFlags |= HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; |
| } |
| #else |
| connect.ConnectionFlags |= HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; |
| #endif |
| |
| /* connect to control service */ |
| connect.ServiceID = HTT_DATA_MSG_SVC; |
| |
| status = HTCConnectService(pdev->htc_pdev, &connect, &response); |
| |
| if (status != A_OK) { |
| return 1; /* failure */ |
| } |
| pdev->htc_endpoint = response.Endpoint; |
| #if defined(HIF_PCI) |
| hif_pci_save_htc_htt_config_endpoint(pdev->htc_endpoint); |
| #endif |
| |
| #ifdef QCA_TX_HTT2_SUPPORT |
| /* Start TX HTT2 service if the target support it. */ |
| if (pdev->cfg.is_high_latency) { |
| adf_os_mem_set(&connect, 0, sizeof(connect)); |
| adf_os_mem_set(&response, 0, sizeof(response)); |
| |
| /* The same as HTT service but no RX. */ |
| connect.EpCallbacks.pContext = pdev; |
| connect.EpCallbacks.EpTxComplete = htt_h2t_send_complete; |
| connect.EpCallbacks.EpSendFull = htt_h2t_full; |
| connect.MaxSendQueueDepth = HTT_MAX_SEND_QUEUE_DEPTH; |
| |
| /* Should NOT support credit flow control. */ |
| connect.ConnectionFlags |= HTC_CONNECT_FLAGS_DISABLE_CREDIT_FLOW_CTRL; |
| /* Enable HTC schedule mechanism for TX HTT2 service. */ |
| connect.ConnectionFlags |= HTC_CONNECT_FLAGS_ENABLE_HTC_SCHEDULE; |
| |
| connect.ServiceID = HTT_DATA2_MSG_SVC; |
| |
| status = HTCConnectService(pdev->htc_pdev, &connect, &response); |
| if (status != A_OK) { |
| pdev->htc_tx_htt2_endpoint = ENDPOINT_UNUSED; |
| pdev->htc_tx_htt2_max_size = 0; |
| } else { |
| pdev->htc_tx_htt2_endpoint = response.Endpoint; |
| pdev->htc_tx_htt2_max_size = HTC_TX_HTT2_MAX_SIZE; |
| } |
| |
| adf_os_print("TX HTT %s, ep %d size %d\n", |
| (status == A_OK ? "ON" : "OFF"), |
| pdev->htc_tx_htt2_endpoint, |
| pdev->htc_tx_htt2_max_size); |
| } |
| #endif /* QCA_TX_HTT2_SUPPORT */ |
| |
| return 0; /* success */ |
| } |
| |
| #if HTT_DEBUG_LEVEL > 5 |
| void |
| htt_display(htt_pdev_handle pdev, int indent) |
| { |
| adf_os_print("%*s%s:\n", indent, " ", "HTT"); |
| adf_os_print( |
| "%*stx desc pool: %d elems of %d bytes, " |
| "%d currently allocated\n", indent+4, " ", |
| pdev->tx_descs.pool_elems, |
| pdev->tx_descs.size, |
| pdev->tx_descs.alloc_cnt); |
| adf_os_print( |
| "%*srx ring: space for %d elems, filled with %d buffers\n", |
| indent+4, " ", |
| pdev->rx_ring.size, |
| pdev->rx_ring.fill_level); |
| adf_os_print("%*sat %pK (%#x paddr)\n", indent+8, " ", |
| pdev->rx_ring.buf.paddrs_ring, |
| pdev->rx_ring.base_paddr); |
| adf_os_print("%*snetbuf ring @ %pK\n", indent+8, " ", |
| pdev->rx_ring.buf.netbufs_ring); |
| adf_os_print("%*sFW_IDX shadow register: vaddr = %pK, paddr = %#x\n", |
| indent+8, " ", |
| pdev->rx_ring.alloc_idx.vaddr, |
| pdev->rx_ring.alloc_idx.paddr); |
| adf_os_print( |
| "%*sSW enqueue index = %d, SW dequeue index: desc = %d, buf = %d\n", |
| indent+8, " ", |
| *pdev->rx_ring.alloc_idx.vaddr, |
| pdev->rx_ring.sw_rd_idx.msdu_desc, |
| pdev->rx_ring.sw_rd_idx.msdu_payld); |
| } |
| #endif |
| |
| /* Disable ASPM : Disable PCIe low power */ |
| void htt_htc_disable_aspm(void) |
| { |
| htc_disable_aspm(); |
| } |
| |
| #ifdef IPA_UC_OFFLOAD |
| /* |
| * Attach resource for micro controller data path |
| */ |
| int |
| htt_ipa_uc_attach(struct htt_pdev_t *pdev) |
| { |
| int error; |
| |
| /* TX resource attach */ |
| error = htt_tx_ipa_uc_attach(pdev, |
| ol_cfg_ipa_uc_tx_buf_size(pdev->ctrl_pdev), |
| ol_cfg_ipa_uc_tx_max_buf_cnt(pdev->ctrl_pdev), |
| ol_cfg_ipa_uc_tx_partition_base(pdev->ctrl_pdev)); |
| if (error) { |
| adf_os_print("HTT IPA UC TX attach fail code %d\n", error); |
| HTT_ASSERT0(0); |
| return error; |
| } |
| |
| /* RX resource attach */ |
| error = htt_rx_ipa_uc_attach(pdev, |
| ol_cfg_ipa_uc_rx_ind_ring_size(pdev->ctrl_pdev)); |
| if (error) { |
| adf_os_print("HTT IPA UC RX attach fail code %d\n", error); |
| htt_tx_ipa_uc_detach(pdev); |
| HTT_ASSERT0(0); |
| return error; |
| } |
| |
| return 0; /* success */ |
| } |
| |
| void |
| htt_ipa_uc_detach(struct htt_pdev_t *pdev) |
| { |
| /* TX IPA micro controller detach */ |
| htt_tx_ipa_uc_detach(pdev); |
| |
| /* RX IPA micro controller detach */ |
| htt_rx_ipa_uc_detach(pdev); |
| } |
| |
| /* |
| * Distribute micro controller resource to control module |
| */ |
| int |
| htt_ipa_uc_get_resource(htt_pdev_handle pdev, |
| u_int32_t *ce_sr_base_paddr, |
| u_int32_t *ce_sr_ring_size, |
| u_int32_t *ce_reg_paddr, |
| u_int32_t *tx_comp_ring_base_paddr, |
| u_int32_t *tx_comp_ring_size, |
| u_int32_t *tx_num_alloc_buffer, |
| u_int32_t *rx_rdy_ring_base_paddr, |
| u_int32_t *rx_rdy_ring_size, |
| u_int32_t *rx_proc_done_idx_paddr) |
| { |
| /* Release allocated resource to client */ |
| *tx_comp_ring_base_paddr = |
| (u_int32_t)pdev->ipa_uc_tx_rsc.tx_comp_base.paddr; |
| *tx_comp_ring_size = |
| (u_int32_t)ol_cfg_ipa_uc_tx_max_buf_cnt(pdev->ctrl_pdev); |
| *tx_num_alloc_buffer = |
| (u_int32_t)pdev->ipa_uc_tx_rsc.alloc_tx_buf_cnt; |
| *rx_rdy_ring_base_paddr = |
| (u_int32_t)pdev->ipa_uc_rx_rsc.rx_ind_ring_base.paddr; |
| *rx_rdy_ring_size = |
| (u_int32_t)pdev->ipa_uc_rx_rsc.rx_ind_ring_size; |
| *rx_proc_done_idx_paddr = |
| (u_int32_t)pdev->ipa_uc_rx_rsc.rx_ipa_prc_done_idx.paddr; |
| |
| /* Get copy engine, bus resource */ |
| HTCIpaGetCEResource(pdev->htc_pdev, |
| ce_sr_base_paddr, ce_sr_ring_size, ce_reg_paddr); |
| |
| |
| return 0; |
| } |
| |
| /* |
| * Distribute micro controller doorbell register to firmware |
| */ |
| int |
| htt_ipa_uc_set_doorbell_paddr(htt_pdev_handle pdev, |
| u_int32_t ipa_uc_tx_doorbell_paddr, |
| u_int32_t ipa_uc_rx_doorbell_paddr) |
| { |
| pdev->ipa_uc_tx_rsc.tx_comp_idx_paddr = ipa_uc_tx_doorbell_paddr; |
| pdev->ipa_uc_rx_rsc.rx_rdy_idx_paddr = ipa_uc_rx_doorbell_paddr; |
| return 0; |
| } |
| #endif /* IPA_UC_OFFLOAD */ |
| |
| #if defined(DEBUG_HL_LOGGING) && defined(CONFIG_HL_SUPPORT) |
| |
| void htt_dump_bundle_stats(htt_pdev_handle pdev) |
| { |
| HTCDumpBundleStats(pdev->htc_pdev); |
| } |
| |
| void htt_clear_bundle_stats(htt_pdev_handle pdev) |
| { |
| HTCClearBundleStats(pdev->htc_pdev); |
| } |
| #endif |
| |
| /** |
| * htt_mark_first_wakeup_packet() - set flag to indicate that |
| * fw is compatible for marking first packet after wow wakeup |
| * @pdev: pointer to htt pdev |
| * @value: 1 for enabled/ 0 for disabled |
| * |
| * Return: None |
| */ |
| void htt_mark_first_wakeup_packet(htt_pdev_handle pdev, |
| uint8_t value) |
| { |
| if (!pdev) { |
| adf_os_print("%s: htt pdev is NULL", __func__); |
| return; |
| } |
| |
| pdev->cfg.is_first_wakeup_packet = value; |
| } |
| |
| |