18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2007-2011 Atheros Communications Inc. 38c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012 Qualcomm Atheros, Inc. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 68c2ecf20Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 78c2ecf20Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 108c2ecf20Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 118c2ecf20Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 128c2ecf20Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 138c2ecf20Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 148c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 158c2ecf20Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "core.h" 198c2ecf20Sopenharmony_ci#include "hif.h" 208c2ecf20Sopenharmony_ci#include "debug.h" 218c2ecf20Sopenharmony_ci#include "hif-ops.h" 228c2ecf20Sopenharmony_ci#include "trace.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define CALC_TXRX_PADDED_LEN(dev, len) (__ALIGN_MASK((len), (dev)->block_mask)) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_cleanup(struct htc_target *target); 298c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_stop(struct htc_target *target); 308c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, 318c2ecf20Sopenharmony_ci struct list_head *pkt_queue); 328c2ecf20Sopenharmony_cistatic void ath6kl_htc_set_credit_dist(struct htc_target *target, 338c2ecf20Sopenharmony_ci struct ath6kl_htc_credit_info *cred_info, 348c2ecf20Sopenharmony_ci u16 svc_pri_order[], int len); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci/* threshold to re-enable Tx bundling for an AC*/ 378c2ecf20Sopenharmony_ci#define TX_RESUME_BUNDLE_THRESHOLD 1500 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Functions for Tx credit handling */ 408c2ecf20Sopenharmony_cistatic void ath6kl_credit_deposit(struct ath6kl_htc_credit_info *cred_info, 418c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *ep_dist, 428c2ecf20Sopenharmony_ci int credits) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit deposit ep %d credits %d\n", 458c2ecf20Sopenharmony_ci ep_dist->endpoint, credits); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci ep_dist->credits += credits; 488c2ecf20Sopenharmony_ci ep_dist->cred_assngd += credits; 498c2ecf20Sopenharmony_ci cred_info->cur_free_credits -= credits; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic void ath6kl_credit_init(struct ath6kl_htc_credit_info *cred_info, 538c2ecf20Sopenharmony_ci struct list_head *ep_list, 548c2ecf20Sopenharmony_ci int tot_credits) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *cur_ep_dist; 578c2ecf20Sopenharmony_ci int count; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit init total %d\n", tot_credits); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci cred_info->cur_free_credits = tot_credits; 628c2ecf20Sopenharmony_ci cred_info->total_avail_credits = tot_credits; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci list_for_each_entry(cur_ep_dist, ep_list, list) { 658c2ecf20Sopenharmony_ci if (cur_ep_dist->endpoint == ENDPOINT_0) 668c2ecf20Sopenharmony_ci continue; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci cur_ep_dist->cred_min = cur_ep_dist->cred_per_msg; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (tot_credits > 4) { 718c2ecf20Sopenharmony_ci if ((cur_ep_dist->svc_id == WMI_DATA_BK_SVC) || 728c2ecf20Sopenharmony_ci (cur_ep_dist->svc_id == WMI_DATA_BE_SVC)) { 738c2ecf20Sopenharmony_ci ath6kl_credit_deposit(cred_info, 748c2ecf20Sopenharmony_ci cur_ep_dist, 758c2ecf20Sopenharmony_ci cur_ep_dist->cred_min); 768c2ecf20Sopenharmony_ci cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) { 818c2ecf20Sopenharmony_ci ath6kl_credit_deposit(cred_info, cur_ep_dist, 828c2ecf20Sopenharmony_ci cur_ep_dist->cred_min); 838c2ecf20Sopenharmony_ci /* 848c2ecf20Sopenharmony_ci * Control service is always marked active, it 858c2ecf20Sopenharmony_ci * never goes inactive EVER. 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_ci cur_ep_dist->dist_flags |= HTC_EP_ACTIVE; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * Streams have to be created (explicit | implicit) for all 928c2ecf20Sopenharmony_ci * kinds of traffic. BE endpoints are also inactive in the 938c2ecf20Sopenharmony_ci * beginning. When BE traffic starts it creates implicit 948c2ecf20Sopenharmony_ci * streams that redistributes credits. 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Note: all other endpoints have minimums set but are 978c2ecf20Sopenharmony_ci * initially given NO credits. credits will be distributed 988c2ecf20Sopenharmony_ci * as traffic activity demands 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* 1038c2ecf20Sopenharmony_ci * For ath6kl_credit_seek function, 1048c2ecf20Sopenharmony_ci * it use list_for_each_entry_reverse to walk around the whole ep list. 1058c2ecf20Sopenharmony_ci * Therefore assign this lowestpri_ep_dist after walk around the ep_list 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci cred_info->lowestpri_ep_dist = cur_ep_dist->list; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci WARN_ON(cred_info->cur_free_credits <= 0); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci list_for_each_entry(cur_ep_dist, ep_list, list) { 1128c2ecf20Sopenharmony_ci if (cur_ep_dist->endpoint == ENDPOINT_0) 1138c2ecf20Sopenharmony_ci continue; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (cur_ep_dist->svc_id == WMI_CONTROL_SVC) { 1168c2ecf20Sopenharmony_ci cur_ep_dist->cred_norm = cur_ep_dist->cred_per_msg; 1178c2ecf20Sopenharmony_ci } else { 1188c2ecf20Sopenharmony_ci /* 1198c2ecf20Sopenharmony_ci * For the remaining data endpoints, we assume that 1208c2ecf20Sopenharmony_ci * each cred_per_msg are the same. We use a simple 1218c2ecf20Sopenharmony_ci * calculation here, we take the remaining credits 1228c2ecf20Sopenharmony_ci * and determine how many max messages this can 1238c2ecf20Sopenharmony_ci * cover and then set each endpoint's normal value 1248c2ecf20Sopenharmony_ci * equal to 3/4 this amount. 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_ci count = (cred_info->cur_free_credits / 1278c2ecf20Sopenharmony_ci cur_ep_dist->cred_per_msg) 1288c2ecf20Sopenharmony_ci * cur_ep_dist->cred_per_msg; 1298c2ecf20Sopenharmony_ci count = (count * 3) >> 2; 1308c2ecf20Sopenharmony_ci count = max(count, cur_ep_dist->cred_per_msg); 1318c2ecf20Sopenharmony_ci cur_ep_dist->cred_norm = count; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, 1358c2ecf20Sopenharmony_ci "credit ep %d svc_id %d credits %d per_msg %d norm %d min %d\n", 1368c2ecf20Sopenharmony_ci cur_ep_dist->endpoint, 1378c2ecf20Sopenharmony_ci cur_ep_dist->svc_id, 1388c2ecf20Sopenharmony_ci cur_ep_dist->credits, 1398c2ecf20Sopenharmony_ci cur_ep_dist->cred_per_msg, 1408c2ecf20Sopenharmony_ci cur_ep_dist->cred_norm, 1418c2ecf20Sopenharmony_ci cur_ep_dist->cred_min); 1428c2ecf20Sopenharmony_ci } 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* initialize and setup credit distribution */ 1468c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_credit_setup(struct htc_target *htc_target, 1478c2ecf20Sopenharmony_ci struct ath6kl_htc_credit_info *cred_info) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci u16 servicepriority[5]; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci memset(cred_info, 0, sizeof(struct ath6kl_htc_credit_info)); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci servicepriority[0] = WMI_CONTROL_SVC; /* highest */ 1548c2ecf20Sopenharmony_ci servicepriority[1] = WMI_DATA_VO_SVC; 1558c2ecf20Sopenharmony_ci servicepriority[2] = WMI_DATA_VI_SVC; 1568c2ecf20Sopenharmony_ci servicepriority[3] = WMI_DATA_BE_SVC; 1578c2ecf20Sopenharmony_ci servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* set priority list */ 1608c2ecf20Sopenharmony_ci ath6kl_htc_set_credit_dist(htc_target, cred_info, servicepriority, 5); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/* reduce an ep's credits back to a set limit */ 1668c2ecf20Sopenharmony_cistatic void ath6kl_credit_reduce(struct ath6kl_htc_credit_info *cred_info, 1678c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *ep_dist, 1688c2ecf20Sopenharmony_ci int limit) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int credits; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit reduce ep %d limit %d\n", 1738c2ecf20Sopenharmony_ci ep_dist->endpoint, limit); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci ep_dist->cred_assngd = limit; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (ep_dist->credits <= limit) 1788c2ecf20Sopenharmony_ci return; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci credits = ep_dist->credits - limit; 1818c2ecf20Sopenharmony_ci ep_dist->credits -= credits; 1828c2ecf20Sopenharmony_ci cred_info->cur_free_credits += credits; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic void ath6kl_credit_update(struct ath6kl_htc_credit_info *cred_info, 1868c2ecf20Sopenharmony_ci struct list_head *epdist_list) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *cur_list; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci list_for_each_entry(cur_list, epdist_list, list) { 1918c2ecf20Sopenharmony_ci if (cur_list->endpoint == ENDPOINT_0) 1928c2ecf20Sopenharmony_ci continue; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (cur_list->cred_to_dist > 0) { 1958c2ecf20Sopenharmony_ci cur_list->credits += cur_list->cred_to_dist; 1968c2ecf20Sopenharmony_ci cur_list->cred_to_dist = 0; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (cur_list->credits > cur_list->cred_assngd) 1998c2ecf20Sopenharmony_ci ath6kl_credit_reduce(cred_info, 2008c2ecf20Sopenharmony_ci cur_list, 2018c2ecf20Sopenharmony_ci cur_list->cred_assngd); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (cur_list->credits > cur_list->cred_norm) 2048c2ecf20Sopenharmony_ci ath6kl_credit_reduce(cred_info, cur_list, 2058c2ecf20Sopenharmony_ci cur_list->cred_norm); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci if (!(cur_list->dist_flags & HTC_EP_ACTIVE)) { 2088c2ecf20Sopenharmony_ci if (cur_list->txq_depth == 0) 2098c2ecf20Sopenharmony_ci ath6kl_credit_reduce(cred_info, 2108c2ecf20Sopenharmony_ci cur_list, 0); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* 2178c2ecf20Sopenharmony_ci * HTC has an endpoint that needs credits, ep_dist is the endpoint in 2188c2ecf20Sopenharmony_ci * question. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic void ath6kl_credit_seek(struct ath6kl_htc_credit_info *cred_info, 2218c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *ep_dist) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *curdist_list; 2248c2ecf20Sopenharmony_ci int credits = 0; 2258c2ecf20Sopenharmony_ci int need; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (ep_dist->svc_id == WMI_CONTROL_SVC) 2288c2ecf20Sopenharmony_ci goto out; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if ((ep_dist->svc_id == WMI_DATA_VI_SVC) || 2318c2ecf20Sopenharmony_ci (ep_dist->svc_id == WMI_DATA_VO_SVC)) 2328c2ecf20Sopenharmony_ci if ((ep_dist->cred_assngd >= ep_dist->cred_norm)) 2338c2ecf20Sopenharmony_ci goto out; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* 2368c2ecf20Sopenharmony_ci * For all other services, we follow a simple algorithm of: 2378c2ecf20Sopenharmony_ci * 2388c2ecf20Sopenharmony_ci * 1. checking the free pool for credits 2398c2ecf20Sopenharmony_ci * 2. checking lower priority endpoints for credits to take 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci if (credits >= ep_dist->seek_cred) 2458c2ecf20Sopenharmony_ci goto out; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* 2488c2ecf20Sopenharmony_ci * We don't have enough in the free pool, try taking away from 2498c2ecf20Sopenharmony_ci * lower priority services The rule for taking away credits: 2508c2ecf20Sopenharmony_ci * 2518c2ecf20Sopenharmony_ci * 1. Only take from lower priority endpoints 2528c2ecf20Sopenharmony_ci * 2. Only take what is allocated above the minimum (never 2538c2ecf20Sopenharmony_ci * starve an endpoint completely) 2548c2ecf20Sopenharmony_ci * 3. Only take what you need. 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci list_for_each_entry_reverse(curdist_list, 2588c2ecf20Sopenharmony_ci &cred_info->lowestpri_ep_dist, 2598c2ecf20Sopenharmony_ci list) { 2608c2ecf20Sopenharmony_ci if (curdist_list == ep_dist) 2618c2ecf20Sopenharmony_ci break; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci need = ep_dist->seek_cred - cred_info->cur_free_credits; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci if ((curdist_list->cred_assngd - need) >= 2668c2ecf20Sopenharmony_ci curdist_list->cred_min) { 2678c2ecf20Sopenharmony_ci /* 2688c2ecf20Sopenharmony_ci * The current one has been allocated more than 2698c2ecf20Sopenharmony_ci * it's minimum and it has enough credits assigned 2708c2ecf20Sopenharmony_ci * above it's minimum to fulfill our need try to 2718c2ecf20Sopenharmony_ci * take away just enough to fulfill our need. 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci ath6kl_credit_reduce(cred_info, curdist_list, 2748c2ecf20Sopenharmony_ci curdist_list->cred_assngd - need); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (cred_info->cur_free_credits >= 2778c2ecf20Sopenharmony_ci ep_dist->seek_cred) 2788c2ecf20Sopenharmony_ci break; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci if (curdist_list->endpoint == ENDPOINT_0) 2828c2ecf20Sopenharmony_ci break; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci credits = min(cred_info->cur_free_credits, ep_dist->seek_cred); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ciout: 2888c2ecf20Sopenharmony_ci /* did we find some credits? */ 2898c2ecf20Sopenharmony_ci if (credits) 2908c2ecf20Sopenharmony_ci ath6kl_credit_deposit(cred_info, ep_dist, credits); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci ep_dist->seek_cred = 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/* redistribute credits based on activity change */ 2968c2ecf20Sopenharmony_cistatic void ath6kl_credit_redistribute(struct ath6kl_htc_credit_info *info, 2978c2ecf20Sopenharmony_ci struct list_head *ep_dist_list) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *curdist_list; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci list_for_each_entry(curdist_list, ep_dist_list, list) { 3028c2ecf20Sopenharmony_ci if (curdist_list->endpoint == ENDPOINT_0) 3038c2ecf20Sopenharmony_ci continue; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if ((curdist_list->svc_id == WMI_DATA_BK_SVC) || 3068c2ecf20Sopenharmony_ci (curdist_list->svc_id == WMI_DATA_BE_SVC)) 3078c2ecf20Sopenharmony_ci curdist_list->dist_flags |= HTC_EP_ACTIVE; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if ((curdist_list->svc_id != WMI_CONTROL_SVC) && 3108c2ecf20Sopenharmony_ci !(curdist_list->dist_flags & HTC_EP_ACTIVE)) { 3118c2ecf20Sopenharmony_ci if (curdist_list->txq_depth == 0) 3128c2ecf20Sopenharmony_ci ath6kl_credit_reduce(info, curdist_list, 0); 3138c2ecf20Sopenharmony_ci else 3148c2ecf20Sopenharmony_ci ath6kl_credit_reduce(info, 3158c2ecf20Sopenharmony_ci curdist_list, 3168c2ecf20Sopenharmony_ci curdist_list->cred_min); 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* 3228c2ecf20Sopenharmony_ci * 3238c2ecf20Sopenharmony_ci * This function is invoked whenever endpoints require credit 3248c2ecf20Sopenharmony_ci * distributions. A lock is held while this function is invoked, this 3258c2ecf20Sopenharmony_ci * function shall NOT block. The ep_dist_list is a list of distribution 3268c2ecf20Sopenharmony_ci * structures in prioritized order as defined by the call to the 3278c2ecf20Sopenharmony_ci * htc_set_credit_dist() api. 3288c2ecf20Sopenharmony_ci */ 3298c2ecf20Sopenharmony_cistatic void ath6kl_credit_distribute(struct ath6kl_htc_credit_info *cred_info, 3308c2ecf20Sopenharmony_ci struct list_head *ep_dist_list, 3318c2ecf20Sopenharmony_ci enum htc_credit_dist_reason reason) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci switch (reason) { 3348c2ecf20Sopenharmony_ci case HTC_CREDIT_DIST_SEND_COMPLETE: 3358c2ecf20Sopenharmony_ci ath6kl_credit_update(cred_info, ep_dist_list); 3368c2ecf20Sopenharmony_ci break; 3378c2ecf20Sopenharmony_ci case HTC_CREDIT_DIST_ACTIVITY_CHANGE: 3388c2ecf20Sopenharmony_ci ath6kl_credit_redistribute(cred_info, ep_dist_list); 3398c2ecf20Sopenharmony_ci break; 3408c2ecf20Sopenharmony_ci default: 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci WARN_ON(cred_info->cur_free_credits > cred_info->total_avail_credits); 3458c2ecf20Sopenharmony_ci WARN_ON(cred_info->cur_free_credits < 0); 3468c2ecf20Sopenharmony_ci} 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_cistatic void ath6kl_htc_tx_buf_align(u8 **buf, unsigned long len) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci u8 *align_addr; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (!IS_ALIGNED((unsigned long) *buf, 4)) { 3538c2ecf20Sopenharmony_ci align_addr = PTR_ALIGN(*buf - 4, 4); 3548c2ecf20Sopenharmony_ci memmove(align_addr, *buf, len); 3558c2ecf20Sopenharmony_ci *buf = align_addr; 3568c2ecf20Sopenharmony_ci } 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic void ath6kl_htc_tx_prep_pkt(struct htc_packet *packet, u8 flags, 3608c2ecf20Sopenharmony_ci int ctrl0, int ctrl1) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct htc_frame_hdr *hdr; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci packet->buf -= HTC_HDR_LENGTH; 3658c2ecf20Sopenharmony_ci hdr = (struct htc_frame_hdr *)packet->buf; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci /* Endianess? */ 3688c2ecf20Sopenharmony_ci put_unaligned((u16)packet->act_len, &hdr->payld_len); 3698c2ecf20Sopenharmony_ci hdr->flags = flags; 3708c2ecf20Sopenharmony_ci hdr->eid = packet->endpoint; 3718c2ecf20Sopenharmony_ci hdr->ctrl[0] = ctrl0; 3728c2ecf20Sopenharmony_ci hdr->ctrl[1] = ctrl1; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void htc_reclaim_txctrl_buf(struct htc_target *target, 3768c2ecf20Sopenharmony_ci struct htc_packet *pkt) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci spin_lock_bh(&target->htc_lock); 3798c2ecf20Sopenharmony_ci list_add_tail(&pkt->list, &target->free_ctrl_txbuf); 3808c2ecf20Sopenharmony_ci spin_unlock_bh(&target->htc_lock); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct htc_packet *htc_get_control_buf(struct htc_target *target, 3848c2ecf20Sopenharmony_ci bool tx) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct htc_packet *packet = NULL; 3878c2ecf20Sopenharmony_ci struct list_head *buf_list; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci buf_list = tx ? &target->free_ctrl_txbuf : &target->free_ctrl_rxbuf; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci spin_lock_bh(&target->htc_lock); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci if (list_empty(buf_list)) { 3948c2ecf20Sopenharmony_ci spin_unlock_bh(&target->htc_lock); 3958c2ecf20Sopenharmony_ci return NULL; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci packet = list_first_entry(buf_list, struct htc_packet, list); 3998c2ecf20Sopenharmony_ci list_del(&packet->list); 4008c2ecf20Sopenharmony_ci spin_unlock_bh(&target->htc_lock); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (tx) 4038c2ecf20Sopenharmony_ci packet->buf = packet->buf_start + HTC_HDR_LENGTH; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return packet; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic void htc_tx_comp_update(struct htc_target *target, 4098c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 4108c2ecf20Sopenharmony_ci struct htc_packet *packet) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci packet->completion = NULL; 4138c2ecf20Sopenharmony_ci packet->buf += HTC_HDR_LENGTH; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (!packet->status) 4168c2ecf20Sopenharmony_ci return; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ath6kl_err("req failed (status:%d, ep:%d, len:%d creds:%d)\n", 4198c2ecf20Sopenharmony_ci packet->status, packet->endpoint, packet->act_len, 4208c2ecf20Sopenharmony_ci packet->info.tx.cred_used); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* on failure to submit, reclaim credits for this packet */ 4238c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 4248c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_to_dist += 4258c2ecf20Sopenharmony_ci packet->info.tx.cred_used; 4268c2ecf20Sopenharmony_ci endpoint->cred_dist.txq_depth = get_queue_depth(&endpoint->txq); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx ctxt 0x%p dist 0x%p\n", 4298c2ecf20Sopenharmony_ci target->credit_info, &target->cred_dist_list); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci ath6kl_credit_distribute(target->credit_info, 4328c2ecf20Sopenharmony_ci &target->cred_dist_list, 4338c2ecf20Sopenharmony_ci HTC_CREDIT_DIST_SEND_COMPLETE); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void htc_tx_complete(struct htc_endpoint *endpoint, 4398c2ecf20Sopenharmony_ci struct list_head *txq) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci if (list_empty(txq)) 4428c2ecf20Sopenharmony_ci return; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 4458c2ecf20Sopenharmony_ci "htc tx complete ep %d pkts %d\n", 4468c2ecf20Sopenharmony_ci endpoint->eid, get_queue_depth(txq)); 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ath6kl_tx_complete(endpoint->target, txq); 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_cistatic void htc_tx_comp_handler(struct htc_target *target, 4528c2ecf20Sopenharmony_ci struct htc_packet *packet) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint = &target->endpoint[packet->endpoint]; 4558c2ecf20Sopenharmony_ci struct list_head container; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx complete seqno %d\n", 4588c2ecf20Sopenharmony_ci packet->info.tx.seqno); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci htc_tx_comp_update(target, endpoint, packet); 4618c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&container); 4628c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &container); 4638c2ecf20Sopenharmony_ci /* do completion */ 4648c2ecf20Sopenharmony_ci htc_tx_complete(endpoint, &container); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_cistatic void htc_async_tx_scat_complete(struct htc_target *target, 4688c2ecf20Sopenharmony_ci struct hif_scatter_req *scat_req) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 4718c2ecf20Sopenharmony_ci struct htc_packet *packet; 4728c2ecf20Sopenharmony_ci struct list_head tx_compq; 4738c2ecf20Sopenharmony_ci int i; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tx_compq); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 4788c2ecf20Sopenharmony_ci "htc tx scat complete len %d entries %d\n", 4798c2ecf20Sopenharmony_ci scat_req->len, scat_req->scat_entries); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (scat_req->status) 4828c2ecf20Sopenharmony_ci ath6kl_err("send scatter req failed: %d\n", scat_req->status); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci packet = scat_req->scat_list[0].packet; 4858c2ecf20Sopenharmony_ci endpoint = &target->endpoint[packet->endpoint]; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* walk through the scatter list and process */ 4888c2ecf20Sopenharmony_ci for (i = 0; i < scat_req->scat_entries; i++) { 4898c2ecf20Sopenharmony_ci packet = scat_req->scat_list[i].packet; 4908c2ecf20Sopenharmony_ci if (!packet) { 4918c2ecf20Sopenharmony_ci WARN_ON(1); 4928c2ecf20Sopenharmony_ci return; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci packet->status = scat_req->status; 4968c2ecf20Sopenharmony_ci htc_tx_comp_update(target, endpoint, packet); 4978c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &tx_compq); 4988c2ecf20Sopenharmony_ci } 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci /* free scatter request */ 5018c2ecf20Sopenharmony_ci hif_scatter_req_add(target->dev->ar, scat_req); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci /* complete all packets */ 5048c2ecf20Sopenharmony_ci htc_tx_complete(endpoint, &tx_compq); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int ath6kl_htc_tx_issue(struct htc_target *target, 5088c2ecf20Sopenharmony_ci struct htc_packet *packet) 5098c2ecf20Sopenharmony_ci{ 5108c2ecf20Sopenharmony_ci int status; 5118c2ecf20Sopenharmony_ci bool sync = false; 5128c2ecf20Sopenharmony_ci u32 padded_len, send_len; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (!packet->completion) 5158c2ecf20Sopenharmony_ci sync = true; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci send_len = packet->act_len + HTC_HDR_LENGTH; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci padded_len = CALC_TXRX_PADDED_LEN(target, send_len); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 5228c2ecf20Sopenharmony_ci "htc tx issue len %d seqno %d padded_len %d mbox 0x%X %s\n", 5238c2ecf20Sopenharmony_ci send_len, packet->info.tx.seqno, padded_len, 5248c2ecf20Sopenharmony_ci target->dev->ar->mbox_info.htc_addr, 5258c2ecf20Sopenharmony_ci sync ? "sync" : "async"); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (sync) { 5288c2ecf20Sopenharmony_ci status = hif_read_write_sync(target->dev->ar, 5298c2ecf20Sopenharmony_ci target->dev->ar->mbox_info.htc_addr, 5308c2ecf20Sopenharmony_ci packet->buf, padded_len, 5318c2ecf20Sopenharmony_ci HIF_WR_SYNC_BLOCK_INC); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci packet->status = status; 5348c2ecf20Sopenharmony_ci packet->buf += HTC_HDR_LENGTH; 5358c2ecf20Sopenharmony_ci } else 5368c2ecf20Sopenharmony_ci status = hif_write_async(target->dev->ar, 5378c2ecf20Sopenharmony_ci target->dev->ar->mbox_info.htc_addr, 5388c2ecf20Sopenharmony_ci packet->buf, padded_len, 5398c2ecf20Sopenharmony_ci HIF_WR_ASYNC_BLOCK_INC, packet); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci trace_ath6kl_htc_tx(status, packet->endpoint, packet->buf, send_len); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return status; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_cistatic int htc_check_credits(struct htc_target *target, 5478c2ecf20Sopenharmony_ci struct htc_endpoint *ep, u8 *flags, 5488c2ecf20Sopenharmony_ci enum htc_endpoint_id eid, unsigned int len, 5498c2ecf20Sopenharmony_ci int *req_cred) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci *req_cred = (len > target->tgt_cred_sz) ? 5528c2ecf20Sopenharmony_ci DIV_ROUND_UP(len, target->tgt_cred_sz) : 1; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, "credit check need %d got %d\n", 5558c2ecf20Sopenharmony_ci *req_cred, ep->cred_dist.credits); 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci if (ep->cred_dist.credits < *req_cred) { 5588c2ecf20Sopenharmony_ci if (eid == ENDPOINT_0) 5598c2ecf20Sopenharmony_ci return -EINVAL; 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* Seek more credits */ 5628c2ecf20Sopenharmony_ci ep->cred_dist.seek_cred = *req_cred - ep->cred_dist.credits; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci ath6kl_credit_seek(target->credit_info, &ep->cred_dist); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci ep->cred_dist.seek_cred = 0; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci if (ep->cred_dist.credits < *req_cred) { 5698c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, 5708c2ecf20Sopenharmony_ci "credit not found for ep %d\n", 5718c2ecf20Sopenharmony_ci eid); 5728c2ecf20Sopenharmony_ci return -EINVAL; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ep->cred_dist.credits -= *req_cred; 5778c2ecf20Sopenharmony_ci ep->ep_st.cred_cosumd += *req_cred; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci /* When we are getting low on credits, ask for more */ 5808c2ecf20Sopenharmony_ci if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { 5818c2ecf20Sopenharmony_ci ep->cred_dist.seek_cred = 5828c2ecf20Sopenharmony_ci ep->cred_dist.cred_per_msg - ep->cred_dist.credits; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci ath6kl_credit_seek(target->credit_info, &ep->cred_dist); 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci /* see if we were successful in getting more */ 5878c2ecf20Sopenharmony_ci if (ep->cred_dist.credits < ep->cred_dist.cred_per_msg) { 5888c2ecf20Sopenharmony_ci /* tell the target we need credits ASAP! */ 5898c2ecf20Sopenharmony_ci *flags |= HTC_FLAGS_NEED_CREDIT_UPDATE; 5908c2ecf20Sopenharmony_ci ep->ep_st.cred_low_indicate += 1; 5918c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, 5928c2ecf20Sopenharmony_ci "credit we need credits asap\n"); 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic void ath6kl_htc_tx_pkts_get(struct htc_target *target, 6008c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 6018c2ecf20Sopenharmony_ci struct list_head *queue) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci int req_cred; 6048c2ecf20Sopenharmony_ci u8 flags; 6058c2ecf20Sopenharmony_ci struct htc_packet *packet; 6068c2ecf20Sopenharmony_ci unsigned int len; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci while (true) { 6098c2ecf20Sopenharmony_ci flags = 0; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (list_empty(&endpoint->txq)) 6128c2ecf20Sopenharmony_ci break; 6138c2ecf20Sopenharmony_ci packet = list_first_entry(&endpoint->txq, struct htc_packet, 6148c2ecf20Sopenharmony_ci list); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 6178c2ecf20Sopenharmony_ci "htc tx got packet 0x%p queue depth %d\n", 6188c2ecf20Sopenharmony_ci packet, get_queue_depth(&endpoint->txq)); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci len = CALC_TXRX_PADDED_LEN(target, 6218c2ecf20Sopenharmony_ci packet->act_len + HTC_HDR_LENGTH); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (htc_check_credits(target, endpoint, &flags, 6248c2ecf20Sopenharmony_ci packet->endpoint, len, &req_cred)) 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci /* now we can fully move onto caller's queue */ 6288c2ecf20Sopenharmony_ci packet = list_first_entry(&endpoint->txq, struct htc_packet, 6298c2ecf20Sopenharmony_ci list); 6308c2ecf20Sopenharmony_ci list_move_tail(&packet->list, queue); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci /* save the number of credits this packet consumed */ 6338c2ecf20Sopenharmony_ci packet->info.tx.cred_used = req_cred; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* all TX packets are handled asynchronously */ 6368c2ecf20Sopenharmony_ci packet->completion = htc_tx_comp_handler; 6378c2ecf20Sopenharmony_ci packet->context = target; 6388c2ecf20Sopenharmony_ci endpoint->ep_st.tx_issued += 1; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci /* save send flags */ 6418c2ecf20Sopenharmony_ci packet->info.tx.flags = flags; 6428c2ecf20Sopenharmony_ci packet->info.tx.seqno = endpoint->seqno; 6438c2ecf20Sopenharmony_ci endpoint->seqno++; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci/* See if the padded tx length falls on a credit boundary */ 6488c2ecf20Sopenharmony_cistatic int htc_get_credit_padding(unsigned int cred_sz, int *len, 6498c2ecf20Sopenharmony_ci struct htc_endpoint *ep) 6508c2ecf20Sopenharmony_ci{ 6518c2ecf20Sopenharmony_ci int rem_cred, cred_pad; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci rem_cred = *len % cred_sz; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci /* No padding needed */ 6568c2ecf20Sopenharmony_ci if (!rem_cred) 6578c2ecf20Sopenharmony_ci return 0; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (!(ep->conn_flags & HTC_FLGS_TX_BNDL_PAD_EN)) 6608c2ecf20Sopenharmony_ci return -1; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* 6638c2ecf20Sopenharmony_ci * The transfer consumes a "partial" credit, this 6648c2ecf20Sopenharmony_ci * packet cannot be bundled unless we add 6658c2ecf20Sopenharmony_ci * additional "dummy" padding (max 255 bytes) to 6668c2ecf20Sopenharmony_ci * consume the entire credit. 6678c2ecf20Sopenharmony_ci */ 6688c2ecf20Sopenharmony_ci cred_pad = *len < cred_sz ? (cred_sz - *len) : rem_cred; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if ((cred_pad > 0) && (cred_pad <= 255)) 6718c2ecf20Sopenharmony_ci *len += cred_pad; 6728c2ecf20Sopenharmony_ci else 6738c2ecf20Sopenharmony_ci /* The amount of padding is too large, send as non-bundled */ 6748c2ecf20Sopenharmony_ci return -1; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci return cred_pad; 6778c2ecf20Sopenharmony_ci} 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_cistatic int ath6kl_htc_tx_setup_scat_list(struct htc_target *target, 6808c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 6818c2ecf20Sopenharmony_ci struct hif_scatter_req *scat_req, 6828c2ecf20Sopenharmony_ci int n_scat, 6838c2ecf20Sopenharmony_ci struct list_head *queue) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct htc_packet *packet; 6868c2ecf20Sopenharmony_ci int i, len, rem_scat, cred_pad; 6878c2ecf20Sopenharmony_ci int status = 0; 6888c2ecf20Sopenharmony_ci u8 flags; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci rem_scat = target->max_tx_bndl_sz; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci for (i = 0; i < n_scat; i++) { 6938c2ecf20Sopenharmony_ci scat_req->scat_list[i].packet = NULL; 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci if (list_empty(queue)) 6968c2ecf20Sopenharmony_ci break; 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci packet = list_first_entry(queue, struct htc_packet, list); 6998c2ecf20Sopenharmony_ci len = CALC_TXRX_PADDED_LEN(target, 7008c2ecf20Sopenharmony_ci packet->act_len + HTC_HDR_LENGTH); 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci cred_pad = htc_get_credit_padding(target->tgt_cred_sz, 7038c2ecf20Sopenharmony_ci &len, endpoint); 7048c2ecf20Sopenharmony_ci if (cred_pad < 0 || rem_scat < len) { 7058c2ecf20Sopenharmony_ci status = -ENOSPC; 7068c2ecf20Sopenharmony_ci break; 7078c2ecf20Sopenharmony_ci } 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci rem_scat -= len; 7108c2ecf20Sopenharmony_ci /* now remove it from the queue */ 7118c2ecf20Sopenharmony_ci list_del(&packet->list); 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci scat_req->scat_list[i].packet = packet; 7148c2ecf20Sopenharmony_ci /* prepare packet and flag message as part of a send bundle */ 7158c2ecf20Sopenharmony_ci flags = packet->info.tx.flags | HTC_FLAGS_SEND_BUNDLE; 7168c2ecf20Sopenharmony_ci ath6kl_htc_tx_prep_pkt(packet, flags, 7178c2ecf20Sopenharmony_ci cred_pad, packet->info.tx.seqno); 7188c2ecf20Sopenharmony_ci /* Make sure the buffer is 4-byte aligned */ 7198c2ecf20Sopenharmony_ci ath6kl_htc_tx_buf_align(&packet->buf, 7208c2ecf20Sopenharmony_ci packet->act_len + HTC_HDR_LENGTH); 7218c2ecf20Sopenharmony_ci scat_req->scat_list[i].buf = packet->buf; 7228c2ecf20Sopenharmony_ci scat_req->scat_list[i].len = len; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci scat_req->len += len; 7258c2ecf20Sopenharmony_ci scat_req->scat_entries++; 7268c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 7278c2ecf20Sopenharmony_ci "htc tx adding (%d) pkt 0x%p seqno %d len %d remaining %d\n", 7288c2ecf20Sopenharmony_ci i, packet, packet->info.tx.seqno, len, rem_scat); 7298c2ecf20Sopenharmony_ci } 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci /* Roll back scatter setup in case of any failure */ 7328c2ecf20Sopenharmony_ci if (scat_req->scat_entries < HTC_MIN_HTC_MSGS_TO_BUNDLE) { 7338c2ecf20Sopenharmony_ci for (i = scat_req->scat_entries - 1; i >= 0; i--) { 7348c2ecf20Sopenharmony_ci packet = scat_req->scat_list[i].packet; 7358c2ecf20Sopenharmony_ci if (packet) { 7368c2ecf20Sopenharmony_ci packet->buf += HTC_HDR_LENGTH; 7378c2ecf20Sopenharmony_ci list_add(&packet->list, queue); 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci return -EAGAIN; 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci return status; 7448c2ecf20Sopenharmony_ci} 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci/* 7478c2ecf20Sopenharmony_ci * Drain a queue and send as bundles this function may return without fully 7488c2ecf20Sopenharmony_ci * draining the queue when 7498c2ecf20Sopenharmony_ci * 7508c2ecf20Sopenharmony_ci * 1. scatter resources are exhausted 7518c2ecf20Sopenharmony_ci * 2. a message that will consume a partial credit will stop the 7528c2ecf20Sopenharmony_ci * bundling process early 7538c2ecf20Sopenharmony_ci * 3. we drop below the minimum number of messages for a bundle 7548c2ecf20Sopenharmony_ci */ 7558c2ecf20Sopenharmony_cistatic void ath6kl_htc_tx_bundle(struct htc_endpoint *endpoint, 7568c2ecf20Sopenharmony_ci struct list_head *queue, 7578c2ecf20Sopenharmony_ci int *sent_bundle, int *n_bundle_pkts) 7588c2ecf20Sopenharmony_ci{ 7598c2ecf20Sopenharmony_ci struct htc_target *target = endpoint->target; 7608c2ecf20Sopenharmony_ci struct hif_scatter_req *scat_req = NULL; 7618c2ecf20Sopenharmony_ci int n_scat, n_sent_bundle = 0, tot_pkts_bundle = 0, i; 7628c2ecf20Sopenharmony_ci struct htc_packet *packet; 7638c2ecf20Sopenharmony_ci int status; 7648c2ecf20Sopenharmony_ci u32 txb_mask; 7658c2ecf20Sopenharmony_ci u8 ac = WMM_NUM_AC; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_ci if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && 7688c2ecf20Sopenharmony_ci (WMI_CONTROL_SVC != endpoint->svc_id)) 7698c2ecf20Sopenharmony_ci ac = target->dev->ar->ep2ac_map[endpoint->eid]; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci while (true) { 7728c2ecf20Sopenharmony_ci status = 0; 7738c2ecf20Sopenharmony_ci n_scat = get_queue_depth(queue); 7748c2ecf20Sopenharmony_ci n_scat = min(n_scat, target->msg_per_bndl_max); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci if (n_scat < HTC_MIN_HTC_MSGS_TO_BUNDLE) 7778c2ecf20Sopenharmony_ci /* not enough to bundle */ 7788c2ecf20Sopenharmony_ci break; 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci scat_req = hif_scatter_req_get(target->dev->ar); 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (!scat_req) { 7838c2ecf20Sopenharmony_ci /* no scatter resources */ 7848c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 7858c2ecf20Sopenharmony_ci "htc tx no more scatter resources\n"); 7868c2ecf20Sopenharmony_ci break; 7878c2ecf20Sopenharmony_ci } 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci if ((ac < WMM_NUM_AC) && (ac != WMM_AC_BK)) { 7908c2ecf20Sopenharmony_ci if (WMM_AC_BE == ac) 7918c2ecf20Sopenharmony_ci /* 7928c2ecf20Sopenharmony_ci * BE, BK have priorities and bit 7938c2ecf20Sopenharmony_ci * positions reversed 7948c2ecf20Sopenharmony_ci */ 7958c2ecf20Sopenharmony_ci txb_mask = (1 << WMM_AC_BK); 7968c2ecf20Sopenharmony_ci else 7978c2ecf20Sopenharmony_ci /* 7988c2ecf20Sopenharmony_ci * any AC with priority lower than 7998c2ecf20Sopenharmony_ci * itself 8008c2ecf20Sopenharmony_ci */ 8018c2ecf20Sopenharmony_ci txb_mask = ((1 << ac) - 1); 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci /* 8048c2ecf20Sopenharmony_ci * when the scatter request resources drop below a 8058c2ecf20Sopenharmony_ci * certain threshold, disable Tx bundling for all 8068c2ecf20Sopenharmony_ci * AC's with priority lower than the current requesting 8078c2ecf20Sopenharmony_ci * AC. Otherwise re-enable Tx bundling for them 8088c2ecf20Sopenharmony_ci */ 8098c2ecf20Sopenharmony_ci if (scat_req->scat_q_depth < ATH6KL_SCATTER_REQS) 8108c2ecf20Sopenharmony_ci target->tx_bndl_mask &= ~txb_mask; 8118c2ecf20Sopenharmony_ci else 8128c2ecf20Sopenharmony_ci target->tx_bndl_mask |= txb_mask; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx pkts to scatter: %d\n", 8168c2ecf20Sopenharmony_ci n_scat); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci scat_req->len = 0; 8198c2ecf20Sopenharmony_ci scat_req->scat_entries = 0; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci status = ath6kl_htc_tx_setup_scat_list(target, endpoint, 8228c2ecf20Sopenharmony_ci scat_req, n_scat, 8238c2ecf20Sopenharmony_ci queue); 8248c2ecf20Sopenharmony_ci if (status == -EAGAIN) { 8258c2ecf20Sopenharmony_ci hif_scatter_req_add(target->dev->ar, scat_req); 8268c2ecf20Sopenharmony_ci break; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci /* send path is always asynchronous */ 8308c2ecf20Sopenharmony_ci scat_req->complete = htc_async_tx_scat_complete; 8318c2ecf20Sopenharmony_ci n_sent_bundle++; 8328c2ecf20Sopenharmony_ci tot_pkts_bundle += scat_req->scat_entries; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 8358c2ecf20Sopenharmony_ci "htc tx scatter bytes %d entries %d\n", 8368c2ecf20Sopenharmony_ci scat_req->len, scat_req->scat_entries); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci for (i = 0; i < scat_req->scat_entries; i++) { 8398c2ecf20Sopenharmony_ci packet = scat_req->scat_list[i].packet; 8408c2ecf20Sopenharmony_ci trace_ath6kl_htc_tx(packet->status, packet->endpoint, 8418c2ecf20Sopenharmony_ci packet->buf, packet->act_len); 8428c2ecf20Sopenharmony_ci } 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci ath6kl_hif_submit_scat_req(target->dev, scat_req, false); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (status) 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci *sent_bundle = n_sent_bundle; 8518c2ecf20Sopenharmony_ci *n_bundle_pkts = tot_pkts_bundle; 8528c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx bundle sent %d pkts\n", 8538c2ecf20Sopenharmony_ci n_sent_bundle); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci return; 8568c2ecf20Sopenharmony_ci} 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_cistatic void ath6kl_htc_tx_from_queue(struct htc_target *target, 8598c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint) 8608c2ecf20Sopenharmony_ci{ 8618c2ecf20Sopenharmony_ci struct list_head txq; 8628c2ecf20Sopenharmony_ci struct htc_packet *packet; 8638c2ecf20Sopenharmony_ci int bundle_sent; 8648c2ecf20Sopenharmony_ci int n_pkts_bundle; 8658c2ecf20Sopenharmony_ci u8 ac = WMM_NUM_AC; 8668c2ecf20Sopenharmony_ci int status; 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci endpoint->tx_proc_cnt++; 8718c2ecf20Sopenharmony_ci if (endpoint->tx_proc_cnt > 1) { 8728c2ecf20Sopenharmony_ci endpoint->tx_proc_cnt--; 8738c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 8748c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc tx busy\n"); 8758c2ecf20Sopenharmony_ci return; 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci /* 8798c2ecf20Sopenharmony_ci * drain the endpoint TX queue for transmission as long 8808c2ecf20Sopenharmony_ci * as we have enough credits. 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&txq); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci if ((HTC_CTRL_RSVD_SVC != endpoint->svc_id) && 8858c2ecf20Sopenharmony_ci (WMI_CONTROL_SVC != endpoint->svc_id)) 8868c2ecf20Sopenharmony_ci ac = target->dev->ar->ep2ac_map[endpoint->eid]; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci while (true) { 8898c2ecf20Sopenharmony_ci if (list_empty(&endpoint->txq)) 8908c2ecf20Sopenharmony_ci break; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci ath6kl_htc_tx_pkts_get(target, endpoint, &txq); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci if (list_empty(&txq)) 8958c2ecf20Sopenharmony_ci break; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci bundle_sent = 0; 9008c2ecf20Sopenharmony_ci n_pkts_bundle = 0; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci while (true) { 9038c2ecf20Sopenharmony_ci /* try to send a bundle on each pass */ 9048c2ecf20Sopenharmony_ci if ((target->tx_bndl_mask) && 9058c2ecf20Sopenharmony_ci (get_queue_depth(&txq) >= 9068c2ecf20Sopenharmony_ci HTC_MIN_HTC_MSGS_TO_BUNDLE)) { 9078c2ecf20Sopenharmony_ci int temp1 = 0, temp2 = 0; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci /* check if bundling is enabled for an AC */ 9108c2ecf20Sopenharmony_ci if (target->tx_bndl_mask & (1 << ac)) { 9118c2ecf20Sopenharmony_ci ath6kl_htc_tx_bundle(endpoint, &txq, 9128c2ecf20Sopenharmony_ci &temp1, &temp2); 9138c2ecf20Sopenharmony_ci bundle_sent += temp1; 9148c2ecf20Sopenharmony_ci n_pkts_bundle += temp2; 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (list_empty(&txq)) 9198c2ecf20Sopenharmony_ci break; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci packet = list_first_entry(&txq, struct htc_packet, 9228c2ecf20Sopenharmony_ci list); 9238c2ecf20Sopenharmony_ci list_del(&packet->list); 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci ath6kl_htc_tx_prep_pkt(packet, packet->info.tx.flags, 9268c2ecf20Sopenharmony_ci 0, packet->info.tx.seqno); 9278c2ecf20Sopenharmony_ci status = ath6kl_htc_tx_issue(target, packet); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci if (status) { 9308c2ecf20Sopenharmony_ci packet->status = status; 9318c2ecf20Sopenharmony_ci packet->completion(packet->context, packet); 9328c2ecf20Sopenharmony_ci } 9338c2ecf20Sopenharmony_ci } 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci endpoint->ep_st.tx_bundles += bundle_sent; 9388c2ecf20Sopenharmony_ci endpoint->ep_st.tx_pkt_bundled += n_pkts_bundle; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci /* 9418c2ecf20Sopenharmony_ci * if an AC has bundling disabled and no tx bundling 9428c2ecf20Sopenharmony_ci * has occured continously for a certain number of TX, 9438c2ecf20Sopenharmony_ci * enable tx bundling for this AC 9448c2ecf20Sopenharmony_ci */ 9458c2ecf20Sopenharmony_ci if (!bundle_sent) { 9468c2ecf20Sopenharmony_ci if (!(target->tx_bndl_mask & (1 << ac)) && 9478c2ecf20Sopenharmony_ci (ac < WMM_NUM_AC)) { 9488c2ecf20Sopenharmony_ci if (++target->ac_tx_count[ac] >= 9498c2ecf20Sopenharmony_ci TX_RESUME_BUNDLE_THRESHOLD) { 9508c2ecf20Sopenharmony_ci target->ac_tx_count[ac] = 0; 9518c2ecf20Sopenharmony_ci target->tx_bndl_mask |= (1 << ac); 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci } 9548c2ecf20Sopenharmony_ci } else { 9558c2ecf20Sopenharmony_ci /* tx bundling will reset the counter */ 9568c2ecf20Sopenharmony_ci if (ac < WMM_NUM_AC) 9578c2ecf20Sopenharmony_ci target->ac_tx_count[ac] = 0; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci } 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci endpoint->tx_proc_cnt = 0; 9628c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 9638c2ecf20Sopenharmony_ci} 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_cistatic bool ath6kl_htc_tx_try(struct htc_target *target, 9668c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 9678c2ecf20Sopenharmony_ci struct htc_packet *tx_pkt) 9688c2ecf20Sopenharmony_ci{ 9698c2ecf20Sopenharmony_ci struct htc_ep_callbacks ep_cb; 9708c2ecf20Sopenharmony_ci int txq_depth; 9718c2ecf20Sopenharmony_ci bool overflow = false; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci ep_cb = endpoint->ep_cb; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 9768c2ecf20Sopenharmony_ci txq_depth = get_queue_depth(&endpoint->txq); 9778c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (txq_depth >= endpoint->max_txq_depth) 9808c2ecf20Sopenharmony_ci overflow = true; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci if (overflow) 9838c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 9848c2ecf20Sopenharmony_ci "htc tx overflow ep %d depth %d max %d\n", 9858c2ecf20Sopenharmony_ci endpoint->eid, txq_depth, 9868c2ecf20Sopenharmony_ci endpoint->max_txq_depth); 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_ci if (overflow && ep_cb.tx_full) { 9898c2ecf20Sopenharmony_ci if (ep_cb.tx_full(endpoint->target, tx_pkt) == 9908c2ecf20Sopenharmony_ci HTC_SEND_FULL_DROP) { 9918c2ecf20Sopenharmony_ci endpoint->ep_st.tx_dropped += 1; 9928c2ecf20Sopenharmony_ci return false; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci } 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 9978c2ecf20Sopenharmony_ci list_add_tail(&tx_pkt->list, &endpoint->txq); 9988c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci ath6kl_htc_tx_from_queue(target, endpoint); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return true; 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_cistatic void htc_chk_ep_txq(struct htc_target *target) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 10088c2ecf20Sopenharmony_ci struct htc_endpoint_credit_dist *cred_dist; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* 10118c2ecf20Sopenharmony_ci * Run through the credit distribution list to see if there are 10128c2ecf20Sopenharmony_ci * packets queued. NOTE: no locks need to be taken since the 10138c2ecf20Sopenharmony_ci * distribution list is not dynamic (cannot be re-ordered) and we 10148c2ecf20Sopenharmony_ci * are not modifying any state. 10158c2ecf20Sopenharmony_ci */ 10168c2ecf20Sopenharmony_ci list_for_each_entry(cred_dist, &target->cred_dist_list, list) { 10178c2ecf20Sopenharmony_ci endpoint = cred_dist->htc_ep; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 10208c2ecf20Sopenharmony_ci if (!list_empty(&endpoint->txq)) { 10218c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 10228c2ecf20Sopenharmony_ci "htc creds ep %d credits %d pkts %d\n", 10238c2ecf20Sopenharmony_ci cred_dist->endpoint, 10248c2ecf20Sopenharmony_ci endpoint->cred_dist.credits, 10258c2ecf20Sopenharmony_ci get_queue_depth(&endpoint->txq)); 10268c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 10278c2ecf20Sopenharmony_ci /* 10288c2ecf20Sopenharmony_ci * Try to start the stalled queue, this list is 10298c2ecf20Sopenharmony_ci * ordered by priority. If there are credits 10308c2ecf20Sopenharmony_ci * available the highest priority queue will get a 10318c2ecf20Sopenharmony_ci * chance to reclaim credits from lower priority 10328c2ecf20Sopenharmony_ci * ones. 10338c2ecf20Sopenharmony_ci */ 10348c2ecf20Sopenharmony_ci ath6kl_htc_tx_from_queue(target, endpoint); 10358c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 10368c2ecf20Sopenharmony_ci } 10378c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci} 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_cistatic int htc_setup_tx_complete(struct htc_target *target) 10428c2ecf20Sopenharmony_ci{ 10438c2ecf20Sopenharmony_ci struct htc_packet *send_pkt = NULL; 10448c2ecf20Sopenharmony_ci int status; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci send_pkt = htc_get_control_buf(target, true); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (!send_pkt) 10498c2ecf20Sopenharmony_ci return -ENOMEM; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (target->htc_tgt_ver >= HTC_VERSION_2P1) { 10528c2ecf20Sopenharmony_ci struct htc_setup_comp_ext_msg *setup_comp_ext; 10538c2ecf20Sopenharmony_ci u32 flags = 0; 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci setup_comp_ext = 10568c2ecf20Sopenharmony_ci (struct htc_setup_comp_ext_msg *)send_pkt->buf; 10578c2ecf20Sopenharmony_ci memset(setup_comp_ext, 0, sizeof(*setup_comp_ext)); 10588c2ecf20Sopenharmony_ci setup_comp_ext->msg_id = 10598c2ecf20Sopenharmony_ci cpu_to_le16(HTC_MSG_SETUP_COMPLETE_EX_ID); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci if (target->msg_per_bndl_max > 0) { 10628c2ecf20Sopenharmony_ci /* Indicate HTC bundling to the target */ 10638c2ecf20Sopenharmony_ci flags |= HTC_SETUP_COMP_FLG_RX_BNDL_EN; 10648c2ecf20Sopenharmony_ci setup_comp_ext->msg_per_rxbndl = 10658c2ecf20Sopenharmony_ci target->msg_per_bndl_max; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci memcpy(&setup_comp_ext->flags, &flags, 10698c2ecf20Sopenharmony_ci sizeof(setup_comp_ext->flags)); 10708c2ecf20Sopenharmony_ci set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp_ext, 10718c2ecf20Sopenharmony_ci sizeof(struct htc_setup_comp_ext_msg), 10728c2ecf20Sopenharmony_ci ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_ci } else { 10758c2ecf20Sopenharmony_ci struct htc_setup_comp_msg *setup_comp; 10768c2ecf20Sopenharmony_ci setup_comp = (struct htc_setup_comp_msg *)send_pkt->buf; 10778c2ecf20Sopenharmony_ci memset(setup_comp, 0, sizeof(struct htc_setup_comp_msg)); 10788c2ecf20Sopenharmony_ci setup_comp->msg_id = cpu_to_le16(HTC_MSG_SETUP_COMPLETE_ID); 10798c2ecf20Sopenharmony_ci set_htc_pkt_info(send_pkt, NULL, (u8 *) setup_comp, 10808c2ecf20Sopenharmony_ci sizeof(struct htc_setup_comp_msg), 10818c2ecf20Sopenharmony_ci ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); 10828c2ecf20Sopenharmony_ci } 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci /* we want synchronous operation */ 10858c2ecf20Sopenharmony_ci send_pkt->completion = NULL; 10868c2ecf20Sopenharmony_ci ath6kl_htc_tx_prep_pkt(send_pkt, 0, 0, 0); 10878c2ecf20Sopenharmony_ci status = ath6kl_htc_tx_issue(target, send_pkt); 10888c2ecf20Sopenharmony_ci htc_reclaim_txctrl_buf(target, send_pkt); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return status; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_cistatic void ath6kl_htc_set_credit_dist(struct htc_target *target, 10948c2ecf20Sopenharmony_ci struct ath6kl_htc_credit_info *credit_info, 10958c2ecf20Sopenharmony_ci u16 srvc_pri_order[], int list_len) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 10988c2ecf20Sopenharmony_ci int i, ep; 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci target->credit_info = credit_info; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci list_add_tail(&target->endpoint[ENDPOINT_0].cred_dist.list, 11038c2ecf20Sopenharmony_ci &target->cred_dist_list); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci for (i = 0; i < list_len; i++) { 11068c2ecf20Sopenharmony_ci for (ep = ENDPOINT_1; ep < ENDPOINT_MAX; ep++) { 11078c2ecf20Sopenharmony_ci endpoint = &target->endpoint[ep]; 11088c2ecf20Sopenharmony_ci if (endpoint->svc_id == srvc_pri_order[i]) { 11098c2ecf20Sopenharmony_ci list_add_tail(&endpoint->cred_dist.list, 11108c2ecf20Sopenharmony_ci &target->cred_dist_list); 11118c2ecf20Sopenharmony_ci break; 11128c2ecf20Sopenharmony_ci } 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci if (ep >= ENDPOINT_MAX) { 11158c2ecf20Sopenharmony_ci WARN_ON(1); 11168c2ecf20Sopenharmony_ci return; 11178c2ecf20Sopenharmony_ci } 11188c2ecf20Sopenharmony_ci } 11198c2ecf20Sopenharmony_ci} 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_tx(struct htc_target *target, 11228c2ecf20Sopenharmony_ci struct htc_packet *packet) 11238c2ecf20Sopenharmony_ci{ 11248c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 11258c2ecf20Sopenharmony_ci struct list_head queue; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 11288c2ecf20Sopenharmony_ci "htc tx ep id %d buf 0x%p len %d\n", 11298c2ecf20Sopenharmony_ci packet->endpoint, packet->buf, packet->act_len); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (packet->endpoint >= ENDPOINT_MAX) { 11328c2ecf20Sopenharmony_ci WARN_ON(1); 11338c2ecf20Sopenharmony_ci return -EINVAL; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci endpoint = &target->endpoint[packet->endpoint]; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci if (!ath6kl_htc_tx_try(target, endpoint, packet)) { 11398c2ecf20Sopenharmony_ci packet->status = (target->htc_flags & HTC_OP_STATE_STOPPING) ? 11408c2ecf20Sopenharmony_ci -ECANCELED : -ENOSPC; 11418c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&queue); 11428c2ecf20Sopenharmony_ci list_add(&packet->list, &queue); 11438c2ecf20Sopenharmony_ci htc_tx_complete(endpoint, &queue); 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci return 0; 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci/* flush endpoint TX queue */ 11508c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_flush_txep(struct htc_target *target, 11518c2ecf20Sopenharmony_ci enum htc_endpoint_id eid, u16 tag) 11528c2ecf20Sopenharmony_ci{ 11538c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 11548c2ecf20Sopenharmony_ci struct list_head discard_q, container; 11558c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint = &target->endpoint[eid]; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci if (!endpoint->svc_id) { 11588c2ecf20Sopenharmony_ci WARN_ON(1); 11598c2ecf20Sopenharmony_ci return; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* initialize the discard queue */ 11638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&discard_q); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, &endpoint->txq, list) { 11688c2ecf20Sopenharmony_ci if ((tag == HTC_TX_PACKET_TAG_ALL) || 11698c2ecf20Sopenharmony_ci (tag == packet->info.tx.tag)) 11708c2ecf20Sopenharmony_ci list_move_tail(&packet->list, &discard_q); 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 11748c2ecf20Sopenharmony_ci 11758c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, &discard_q, list) { 11768c2ecf20Sopenharmony_ci packet->status = -ECANCELED; 11778c2ecf20Sopenharmony_ci list_del(&packet->list); 11788c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 11798c2ecf20Sopenharmony_ci "htc tx flushing pkt 0x%p len %d ep %d tag 0x%x\n", 11808c2ecf20Sopenharmony_ci packet, packet->act_len, 11818c2ecf20Sopenharmony_ci packet->endpoint, packet->info.tx.tag); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&container); 11848c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &container); 11858c2ecf20Sopenharmony_ci htc_tx_complete(endpoint, &container); 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci} 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_cistatic void ath6kl_htc_flush_txep_all(struct htc_target *target) 11908c2ecf20Sopenharmony_ci{ 11918c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 11928c2ecf20Sopenharmony_ci int i; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci dump_cred_dist_stats(target); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { 11978c2ecf20Sopenharmony_ci endpoint = &target->endpoint[i]; 11988c2ecf20Sopenharmony_ci if (endpoint->svc_id == 0) 11998c2ecf20Sopenharmony_ci /* not in use.. */ 12008c2ecf20Sopenharmony_ci continue; 12018c2ecf20Sopenharmony_ci ath6kl_htc_mbox_flush_txep(target, i, HTC_TX_PACKET_TAG_ALL); 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci} 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_activity_changed(struct htc_target *target, 12068c2ecf20Sopenharmony_ci enum htc_endpoint_id eid, 12078c2ecf20Sopenharmony_ci bool active) 12088c2ecf20Sopenharmony_ci{ 12098c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint = &target->endpoint[eid]; 12108c2ecf20Sopenharmony_ci bool dist = false; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci if (endpoint->svc_id == 0) { 12138c2ecf20Sopenharmony_ci WARN_ON(1); 12148c2ecf20Sopenharmony_ci return; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci if (active) { 12208c2ecf20Sopenharmony_ci if (!(endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE)) { 12218c2ecf20Sopenharmony_ci endpoint->cred_dist.dist_flags |= HTC_EP_ACTIVE; 12228c2ecf20Sopenharmony_ci dist = true; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci } else { 12258c2ecf20Sopenharmony_ci if (endpoint->cred_dist.dist_flags & HTC_EP_ACTIVE) { 12268c2ecf20Sopenharmony_ci endpoint->cred_dist.dist_flags &= ~HTC_EP_ACTIVE; 12278c2ecf20Sopenharmony_ci dist = true; 12288c2ecf20Sopenharmony_ci } 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (dist) { 12328c2ecf20Sopenharmony_ci endpoint->cred_dist.txq_depth = 12338c2ecf20Sopenharmony_ci get_queue_depth(&endpoint->txq); 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 12368c2ecf20Sopenharmony_ci "htc tx activity ctxt 0x%p dist 0x%p\n", 12378c2ecf20Sopenharmony_ci target->credit_info, &target->cred_dist_list); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci ath6kl_credit_distribute(target->credit_info, 12408c2ecf20Sopenharmony_ci &target->cred_dist_list, 12418c2ecf20Sopenharmony_ci HTC_CREDIT_DIST_ACTIVITY_CHANGE); 12428c2ecf20Sopenharmony_ci } 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci if (dist && !active) 12478c2ecf20Sopenharmony_ci htc_chk_ep_txq(target); 12488c2ecf20Sopenharmony_ci} 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci/* HTC Rx */ 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_cistatic inline void ath6kl_htc_rx_update_stats(struct htc_endpoint *endpoint, 12538c2ecf20Sopenharmony_ci int n_look_ahds) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci endpoint->ep_st.rx_pkts++; 12568c2ecf20Sopenharmony_ci if (n_look_ahds == 1) 12578c2ecf20Sopenharmony_ci endpoint->ep_st.rx_lkahds++; 12588c2ecf20Sopenharmony_ci else if (n_look_ahds > 1) 12598c2ecf20Sopenharmony_ci endpoint->ep_st.rx_bundle_lkahd++; 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_cistatic inline bool htc_valid_rx_frame_len(struct htc_target *target, 12638c2ecf20Sopenharmony_ci enum htc_endpoint_id eid, int len) 12648c2ecf20Sopenharmony_ci{ 12658c2ecf20Sopenharmony_ci return (eid == target->dev->ar->ctrl_ep) ? 12668c2ecf20Sopenharmony_ci len <= ATH6KL_BUFFER_SIZE : len <= ATH6KL_AMSDU_BUFFER_SIZE; 12678c2ecf20Sopenharmony_ci} 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_cistatic int htc_add_rxbuf(struct htc_target *target, struct htc_packet *packet) 12708c2ecf20Sopenharmony_ci{ 12718c2ecf20Sopenharmony_ci struct list_head queue; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&queue); 12748c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &queue); 12758c2ecf20Sopenharmony_ci return ath6kl_htc_mbox_add_rxbuf_multiple(target, &queue); 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic void htc_reclaim_rxbuf(struct htc_target *target, 12798c2ecf20Sopenharmony_ci struct htc_packet *packet, 12808c2ecf20Sopenharmony_ci struct htc_endpoint *ep) 12818c2ecf20Sopenharmony_ci{ 12828c2ecf20Sopenharmony_ci if (packet->info.rx.rx_flags & HTC_RX_PKT_NO_RECYCLE) { 12838c2ecf20Sopenharmony_ci htc_rxpkt_reset(packet); 12848c2ecf20Sopenharmony_ci packet->status = -ECANCELED; 12858c2ecf20Sopenharmony_ci ep->ep_cb.rx(ep->target, packet); 12868c2ecf20Sopenharmony_ci } else { 12878c2ecf20Sopenharmony_ci htc_rxpkt_reset(packet); 12888c2ecf20Sopenharmony_ci htc_add_rxbuf((void *)(target), packet); 12898c2ecf20Sopenharmony_ci } 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic void reclaim_rx_ctrl_buf(struct htc_target *target, 12938c2ecf20Sopenharmony_ci struct htc_packet *packet) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci spin_lock_bh(&target->htc_lock); 12968c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &target->free_ctrl_rxbuf); 12978c2ecf20Sopenharmony_ci spin_unlock_bh(&target->htc_lock); 12988c2ecf20Sopenharmony_ci} 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_packet(struct htc_target *target, 13018c2ecf20Sopenharmony_ci struct htc_packet *packet, 13028c2ecf20Sopenharmony_ci u32 rx_len) 13038c2ecf20Sopenharmony_ci{ 13048c2ecf20Sopenharmony_ci struct ath6kl_device *dev = target->dev; 13058c2ecf20Sopenharmony_ci u32 padded_len; 13068c2ecf20Sopenharmony_ci int status; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci padded_len = CALC_TXRX_PADDED_LEN(target, rx_len); 13098c2ecf20Sopenharmony_ci 13108c2ecf20Sopenharmony_ci if (padded_len > packet->buf_len) { 13118c2ecf20Sopenharmony_ci ath6kl_err("not enough receive space for packet - padlen %d recvlen %d bufferlen %d\n", 13128c2ecf20Sopenharmony_ci padded_len, rx_len, packet->buf_len); 13138c2ecf20Sopenharmony_ci return -ENOMEM; 13148c2ecf20Sopenharmony_ci } 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 13178c2ecf20Sopenharmony_ci "htc rx 0x%p hdr 0x%x len %d mbox 0x%x\n", 13188c2ecf20Sopenharmony_ci packet, packet->info.rx.exp_hdr, 13198c2ecf20Sopenharmony_ci padded_len, dev->ar->mbox_info.htc_addr); 13208c2ecf20Sopenharmony_ci 13218c2ecf20Sopenharmony_ci status = hif_read_write_sync(dev->ar, 13228c2ecf20Sopenharmony_ci dev->ar->mbox_info.htc_addr, 13238c2ecf20Sopenharmony_ci packet->buf, padded_len, 13248c2ecf20Sopenharmony_ci HIF_RD_SYNC_BLOCK_FIX); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci packet->status = status; 13278c2ecf20Sopenharmony_ci 13288c2ecf20Sopenharmony_ci return status; 13298c2ecf20Sopenharmony_ci} 13308c2ecf20Sopenharmony_ci 13318c2ecf20Sopenharmony_ci/* 13328c2ecf20Sopenharmony_ci * optimization for recv packets, we can indicate a 13338c2ecf20Sopenharmony_ci * "hint" that there are more single-packets to fetch 13348c2ecf20Sopenharmony_ci * on this endpoint. 13358c2ecf20Sopenharmony_ci */ 13368c2ecf20Sopenharmony_cistatic void ath6kl_htc_rx_set_indicate(u32 lk_ahd, 13378c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 13388c2ecf20Sopenharmony_ci struct htc_packet *packet) 13398c2ecf20Sopenharmony_ci{ 13408c2ecf20Sopenharmony_ci struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)&lk_ahd; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci if (htc_hdr->eid == packet->endpoint) { 13438c2ecf20Sopenharmony_ci if (!list_empty(&endpoint->rx_bufq)) 13448c2ecf20Sopenharmony_ci packet->info.rx.indicat_flags |= 13458c2ecf20Sopenharmony_ci HTC_RX_FLAGS_INDICATE_MORE_PKTS; 13468c2ecf20Sopenharmony_ci } 13478c2ecf20Sopenharmony_ci} 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_cistatic void ath6kl_htc_rx_chk_water_mark(struct htc_endpoint *endpoint) 13508c2ecf20Sopenharmony_ci{ 13518c2ecf20Sopenharmony_ci struct htc_ep_callbacks ep_cb = endpoint->ep_cb; 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci if (ep_cb.rx_refill_thresh > 0) { 13548c2ecf20Sopenharmony_ci spin_lock_bh(&endpoint->target->rx_lock); 13558c2ecf20Sopenharmony_ci if (get_queue_depth(&endpoint->rx_bufq) 13568c2ecf20Sopenharmony_ci < ep_cb.rx_refill_thresh) { 13578c2ecf20Sopenharmony_ci spin_unlock_bh(&endpoint->target->rx_lock); 13588c2ecf20Sopenharmony_ci ep_cb.rx_refill(endpoint->target, endpoint->eid); 13598c2ecf20Sopenharmony_ci return; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci spin_unlock_bh(&endpoint->target->rx_lock); 13628c2ecf20Sopenharmony_ci } 13638c2ecf20Sopenharmony_ci} 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci/* This function is called with rx_lock held */ 13668c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_setup(struct htc_target *target, 13678c2ecf20Sopenharmony_ci struct htc_endpoint *ep, 13688c2ecf20Sopenharmony_ci u32 *lk_ahds, struct list_head *queue, int n_msg) 13698c2ecf20Sopenharmony_ci{ 13708c2ecf20Sopenharmony_ci struct htc_packet *packet; 13718c2ecf20Sopenharmony_ci /* FIXME: type of lk_ahds can't be right */ 13728c2ecf20Sopenharmony_ci struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)lk_ahds; 13738c2ecf20Sopenharmony_ci struct htc_ep_callbacks ep_cb; 13748c2ecf20Sopenharmony_ci int status = 0, j, full_len; 13758c2ecf20Sopenharmony_ci bool no_recycle; 13768c2ecf20Sopenharmony_ci 13778c2ecf20Sopenharmony_ci full_len = CALC_TXRX_PADDED_LEN(target, 13788c2ecf20Sopenharmony_ci le16_to_cpu(htc_hdr->payld_len) + 13798c2ecf20Sopenharmony_ci sizeof(*htc_hdr)); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci if (!htc_valid_rx_frame_len(target, ep->eid, full_len)) { 13828c2ecf20Sopenharmony_ci ath6kl_warn("Rx buffer requested with invalid length htc_hdr:eid %d, flags 0x%x, len %d\n", 13838c2ecf20Sopenharmony_ci htc_hdr->eid, htc_hdr->flags, 13848c2ecf20Sopenharmony_ci le16_to_cpu(htc_hdr->payld_len)); 13858c2ecf20Sopenharmony_ci return -EINVAL; 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci ep_cb = ep->ep_cb; 13898c2ecf20Sopenharmony_ci for (j = 0; j < n_msg; j++) { 13908c2ecf20Sopenharmony_ci /* 13918c2ecf20Sopenharmony_ci * Reset flag, any packets allocated using the 13928c2ecf20Sopenharmony_ci * rx_alloc() API cannot be recycled on 13938c2ecf20Sopenharmony_ci * cleanup,they must be explicitly returned. 13948c2ecf20Sopenharmony_ci */ 13958c2ecf20Sopenharmony_ci no_recycle = false; 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci if (ep_cb.rx_allocthresh && 13988c2ecf20Sopenharmony_ci (full_len > ep_cb.rx_alloc_thresh)) { 13998c2ecf20Sopenharmony_ci ep->ep_st.rx_alloc_thresh_hit += 1; 14008c2ecf20Sopenharmony_ci ep->ep_st.rxalloc_thresh_byte += 14018c2ecf20Sopenharmony_ci le16_to_cpu(htc_hdr->payld_len); 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 14048c2ecf20Sopenharmony_ci no_recycle = true; 14058c2ecf20Sopenharmony_ci 14068c2ecf20Sopenharmony_ci packet = ep_cb.rx_allocthresh(ep->target, ep->eid, 14078c2ecf20Sopenharmony_ci full_len); 14088c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 14098c2ecf20Sopenharmony_ci } else { 14108c2ecf20Sopenharmony_ci /* refill handler is being used */ 14118c2ecf20Sopenharmony_ci if (list_empty(&ep->rx_bufq)) { 14128c2ecf20Sopenharmony_ci if (ep_cb.rx_refill) { 14138c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 14148c2ecf20Sopenharmony_ci ep_cb.rx_refill(ep->target, ep->eid); 14158c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci } 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci if (list_empty(&ep->rx_bufq)) { 14208c2ecf20Sopenharmony_ci packet = NULL; 14218c2ecf20Sopenharmony_ci } else { 14228c2ecf20Sopenharmony_ci packet = list_first_entry(&ep->rx_bufq, 14238c2ecf20Sopenharmony_ci struct htc_packet, list); 14248c2ecf20Sopenharmony_ci list_del(&packet->list); 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (!packet) { 14298c2ecf20Sopenharmony_ci target->rx_st_flags |= HTC_RECV_WAIT_BUFFERS; 14308c2ecf20Sopenharmony_ci target->ep_waiting = ep->eid; 14318c2ecf20Sopenharmony_ci return -ENOSPC; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* clear flags */ 14358c2ecf20Sopenharmony_ci packet->info.rx.rx_flags = 0; 14368c2ecf20Sopenharmony_ci packet->info.rx.indicat_flags = 0; 14378c2ecf20Sopenharmony_ci packet->status = 0; 14388c2ecf20Sopenharmony_ci 14398c2ecf20Sopenharmony_ci if (no_recycle) 14408c2ecf20Sopenharmony_ci /* 14418c2ecf20Sopenharmony_ci * flag that these packets cannot be 14428c2ecf20Sopenharmony_ci * recycled, they have to be returned to 14438c2ecf20Sopenharmony_ci * the user 14448c2ecf20Sopenharmony_ci */ 14458c2ecf20Sopenharmony_ci packet->info.rx.rx_flags |= HTC_RX_PKT_NO_RECYCLE; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci /* Caller needs to free this upon any failure */ 14488c2ecf20Sopenharmony_ci list_add_tail(&packet->list, queue); 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (target->htc_flags & HTC_OP_STATE_STOPPING) { 14518c2ecf20Sopenharmony_ci status = -ECANCELED; 14528c2ecf20Sopenharmony_ci break; 14538c2ecf20Sopenharmony_ci } 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci if (j) { 14568c2ecf20Sopenharmony_ci packet->info.rx.rx_flags |= HTC_RX_PKT_REFRESH_HDR; 14578c2ecf20Sopenharmony_ci packet->info.rx.exp_hdr = 0xFFFFFFFF; 14588c2ecf20Sopenharmony_ci } else 14598c2ecf20Sopenharmony_ci /* set expected look ahead */ 14608c2ecf20Sopenharmony_ci packet->info.rx.exp_hdr = *lk_ahds; 14618c2ecf20Sopenharmony_ci 14628c2ecf20Sopenharmony_ci packet->act_len = le16_to_cpu(htc_hdr->payld_len) + 14638c2ecf20Sopenharmony_ci HTC_HDR_LENGTH; 14648c2ecf20Sopenharmony_ci } 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci return status; 14678c2ecf20Sopenharmony_ci} 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_alloc(struct htc_target *target, 14708c2ecf20Sopenharmony_ci u32 lk_ahds[], int msg, 14718c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint, 14728c2ecf20Sopenharmony_ci struct list_head *queue) 14738c2ecf20Sopenharmony_ci{ 14748c2ecf20Sopenharmony_ci int status = 0; 14758c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 14768c2ecf20Sopenharmony_ci struct htc_frame_hdr *htc_hdr; 14778c2ecf20Sopenharmony_ci int i, n_msg; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 14808c2ecf20Sopenharmony_ci 14818c2ecf20Sopenharmony_ci for (i = 0; i < msg; i++) { 14828c2ecf20Sopenharmony_ci htc_hdr = (struct htc_frame_hdr *)&lk_ahds[i]; 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci if (htc_hdr->eid >= ENDPOINT_MAX) { 14858c2ecf20Sopenharmony_ci ath6kl_err("invalid ep in look-ahead: %d\n", 14868c2ecf20Sopenharmony_ci htc_hdr->eid); 14878c2ecf20Sopenharmony_ci status = -ENOMEM; 14888c2ecf20Sopenharmony_ci break; 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci if (htc_hdr->eid != endpoint->eid) { 14928c2ecf20Sopenharmony_ci ath6kl_err("invalid ep in look-ahead: %d should be : %d (index:%d)\n", 14938c2ecf20Sopenharmony_ci htc_hdr->eid, endpoint->eid, i); 14948c2ecf20Sopenharmony_ci status = -ENOMEM; 14958c2ecf20Sopenharmony_ci break; 14968c2ecf20Sopenharmony_ci } 14978c2ecf20Sopenharmony_ci 14988c2ecf20Sopenharmony_ci if (le16_to_cpu(htc_hdr->payld_len) > HTC_MAX_PAYLOAD_LENGTH) { 14998c2ecf20Sopenharmony_ci ath6kl_err("payload len %d exceeds max htc : %d !\n", 15008c2ecf20Sopenharmony_ci htc_hdr->payld_len, 15018c2ecf20Sopenharmony_ci (u32) HTC_MAX_PAYLOAD_LENGTH); 15028c2ecf20Sopenharmony_ci status = -ENOMEM; 15038c2ecf20Sopenharmony_ci break; 15048c2ecf20Sopenharmony_ci } 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci if (endpoint->svc_id == 0) { 15078c2ecf20Sopenharmony_ci ath6kl_err("ep %d is not connected !\n", htc_hdr->eid); 15088c2ecf20Sopenharmony_ci status = -ENOMEM; 15098c2ecf20Sopenharmony_ci break; 15108c2ecf20Sopenharmony_ci } 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) { 15138c2ecf20Sopenharmony_ci /* 15148c2ecf20Sopenharmony_ci * HTC header indicates that every packet to follow 15158c2ecf20Sopenharmony_ci * has the same padded length so that it can be 15168c2ecf20Sopenharmony_ci * optimally fetched as a full bundle. 15178c2ecf20Sopenharmony_ci */ 15188c2ecf20Sopenharmony_ci n_msg = (htc_hdr->flags & HTC_FLG_RX_BNDL_CNT) >> 15198c2ecf20Sopenharmony_ci HTC_FLG_RX_BNDL_CNT_S; 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_ci /* the count doesn't include the starter frame */ 15228c2ecf20Sopenharmony_ci n_msg++; 15238c2ecf20Sopenharmony_ci if (n_msg > target->msg_per_bndl_max) { 15248c2ecf20Sopenharmony_ci status = -ENOMEM; 15258c2ecf20Sopenharmony_ci break; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci endpoint->ep_st.rx_bundle_from_hdr += 1; 15298c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 15308c2ecf20Sopenharmony_ci "htc rx bundle pkts %d\n", 15318c2ecf20Sopenharmony_ci n_msg); 15328c2ecf20Sopenharmony_ci } else 15338c2ecf20Sopenharmony_ci /* HTC header only indicates 1 message to fetch */ 15348c2ecf20Sopenharmony_ci n_msg = 1; 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_ci /* Setup packet buffers for each message */ 15378c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_setup(target, endpoint, &lk_ahds[i], 15388c2ecf20Sopenharmony_ci queue, n_msg); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci /* 15418c2ecf20Sopenharmony_ci * This is due to unavailabilty of buffers to rx entire data. 15428c2ecf20Sopenharmony_ci * Return no error so that free buffers from queue can be used 15438c2ecf20Sopenharmony_ci * to receive partial data. 15448c2ecf20Sopenharmony_ci */ 15458c2ecf20Sopenharmony_ci if (status == -ENOSPC) { 15468c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 15478c2ecf20Sopenharmony_ci return 0; 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci if (status) 15518c2ecf20Sopenharmony_ci break; 15528c2ecf20Sopenharmony_ci } 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (status) { 15578c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, queue, list) { 15588c2ecf20Sopenharmony_ci list_del(&packet->list); 15598c2ecf20Sopenharmony_ci htc_reclaim_rxbuf(target, packet, 15608c2ecf20Sopenharmony_ci &target->endpoint[packet->endpoint]); 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ci return status; 15658c2ecf20Sopenharmony_ci} 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_cistatic void htc_ctrl_rx(struct htc_target *context, struct htc_packet *packets) 15688c2ecf20Sopenharmony_ci{ 15698c2ecf20Sopenharmony_ci if (packets->endpoint != ENDPOINT_0) { 15708c2ecf20Sopenharmony_ci WARN_ON(1); 15718c2ecf20Sopenharmony_ci return; 15728c2ecf20Sopenharmony_ci } 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (packets->status == -ECANCELED) { 15758c2ecf20Sopenharmony_ci reclaim_rx_ctrl_buf(context, packets); 15768c2ecf20Sopenharmony_ci return; 15778c2ecf20Sopenharmony_ci } 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (packets->act_len > 0) { 15808c2ecf20Sopenharmony_ci ath6kl_err("htc_ctrl_rx, got message with len:%zu\n", 15818c2ecf20Sopenharmony_ci packets->act_len + HTC_HDR_LENGTH); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, 15848c2ecf20Sopenharmony_ci "htc rx unexpected endpoint 0 message", "", 15858c2ecf20Sopenharmony_ci packets->buf - HTC_HDR_LENGTH, 15868c2ecf20Sopenharmony_ci packets->act_len + HTC_HDR_LENGTH); 15878c2ecf20Sopenharmony_ci } 15888c2ecf20Sopenharmony_ci 15898c2ecf20Sopenharmony_ci htc_reclaim_rxbuf(context, packets, &context->endpoint[0]); 15908c2ecf20Sopenharmony_ci} 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cistatic void htc_proc_cred_rpt(struct htc_target *target, 15938c2ecf20Sopenharmony_ci struct htc_credit_report *rpt, 15948c2ecf20Sopenharmony_ci int n_entries, 15958c2ecf20Sopenharmony_ci enum htc_endpoint_id from_ep) 15968c2ecf20Sopenharmony_ci{ 15978c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 15988c2ecf20Sopenharmony_ci int tot_credits = 0, i; 15998c2ecf20Sopenharmony_ci bool dist = false; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci spin_lock_bh(&target->tx_lock); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci for (i = 0; i < n_entries; i++, rpt++) { 16048c2ecf20Sopenharmony_ci if (rpt->eid >= ENDPOINT_MAX) { 16058c2ecf20Sopenharmony_ci WARN_ON(1); 16068c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 16078c2ecf20Sopenharmony_ci return; 16088c2ecf20Sopenharmony_ci } 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci endpoint = &target->endpoint[rpt->eid]; 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_CREDIT, 16138c2ecf20Sopenharmony_ci "credit report ep %d credits %d\n", 16148c2ecf20Sopenharmony_ci rpt->eid, rpt->credits); 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci endpoint->ep_st.tx_cred_rpt += 1; 16178c2ecf20Sopenharmony_ci endpoint->ep_st.cred_retnd += rpt->credits; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_ci if (from_ep == rpt->eid) { 16208c2ecf20Sopenharmony_ci /* 16218c2ecf20Sopenharmony_ci * This credit report arrived on the same endpoint 16228c2ecf20Sopenharmony_ci * indicating it arrived in an RX packet. 16238c2ecf20Sopenharmony_ci */ 16248c2ecf20Sopenharmony_ci endpoint->ep_st.cred_from_rx += rpt->credits; 16258c2ecf20Sopenharmony_ci endpoint->ep_st.cred_rpt_from_rx += 1; 16268c2ecf20Sopenharmony_ci } else if (from_ep == ENDPOINT_0) { 16278c2ecf20Sopenharmony_ci /* credit arrived on endpoint 0 as a NULL message */ 16288c2ecf20Sopenharmony_ci endpoint->ep_st.cred_from_ep0 += rpt->credits; 16298c2ecf20Sopenharmony_ci endpoint->ep_st.cred_rpt_ep0 += 1; 16308c2ecf20Sopenharmony_ci } else { 16318c2ecf20Sopenharmony_ci endpoint->ep_st.cred_from_other += rpt->credits; 16328c2ecf20Sopenharmony_ci endpoint->ep_st.cred_rpt_from_other += 1; 16338c2ecf20Sopenharmony_ci } 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci if (rpt->eid == ENDPOINT_0) 16368c2ecf20Sopenharmony_ci /* always give endpoint 0 credits back */ 16378c2ecf20Sopenharmony_ci endpoint->cred_dist.credits += rpt->credits; 16388c2ecf20Sopenharmony_ci else { 16398c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_to_dist += rpt->credits; 16408c2ecf20Sopenharmony_ci dist = true; 16418c2ecf20Sopenharmony_ci } 16428c2ecf20Sopenharmony_ci 16438c2ecf20Sopenharmony_ci /* 16448c2ecf20Sopenharmony_ci * Refresh tx depth for distribution function that will 16458c2ecf20Sopenharmony_ci * recover these credits NOTE: this is only valid when 16468c2ecf20Sopenharmony_ci * there are credits to recover! 16478c2ecf20Sopenharmony_ci */ 16488c2ecf20Sopenharmony_ci endpoint->cred_dist.txq_depth = 16498c2ecf20Sopenharmony_ci get_queue_depth(&endpoint->txq); 16508c2ecf20Sopenharmony_ci 16518c2ecf20Sopenharmony_ci tot_credits += rpt->credits; 16528c2ecf20Sopenharmony_ci } 16538c2ecf20Sopenharmony_ci 16548c2ecf20Sopenharmony_ci if (dist) { 16558c2ecf20Sopenharmony_ci /* 16568c2ecf20Sopenharmony_ci * This was a credit return based on a completed send 16578c2ecf20Sopenharmony_ci * operations note, this is done with the lock held 16588c2ecf20Sopenharmony_ci */ 16598c2ecf20Sopenharmony_ci ath6kl_credit_distribute(target->credit_info, 16608c2ecf20Sopenharmony_ci &target->cred_dist_list, 16618c2ecf20Sopenharmony_ci HTC_CREDIT_DIST_SEND_COMPLETE); 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci spin_unlock_bh(&target->tx_lock); 16658c2ecf20Sopenharmony_ci 16668c2ecf20Sopenharmony_ci if (tot_credits) 16678c2ecf20Sopenharmony_ci htc_chk_ep_txq(target); 16688c2ecf20Sopenharmony_ci} 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_cistatic int htc_parse_trailer(struct htc_target *target, 16718c2ecf20Sopenharmony_ci struct htc_record_hdr *record, 16728c2ecf20Sopenharmony_ci u8 *record_buf, u32 *next_lk_ahds, 16738c2ecf20Sopenharmony_ci enum htc_endpoint_id endpoint, 16748c2ecf20Sopenharmony_ci int *n_lk_ahds) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci struct htc_bundle_lkahd_rpt *bundle_lkahd_rpt; 16778c2ecf20Sopenharmony_ci struct htc_lookahead_report *lk_ahd; 16788c2ecf20Sopenharmony_ci int len; 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci switch (record->rec_id) { 16818c2ecf20Sopenharmony_ci case HTC_RECORD_CREDITS: 16828c2ecf20Sopenharmony_ci len = record->len / sizeof(struct htc_credit_report); 16838c2ecf20Sopenharmony_ci if (!len) { 16848c2ecf20Sopenharmony_ci WARN_ON(1); 16858c2ecf20Sopenharmony_ci return -EINVAL; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci htc_proc_cred_rpt(target, 16898c2ecf20Sopenharmony_ci (struct htc_credit_report *) record_buf, 16908c2ecf20Sopenharmony_ci len, endpoint); 16918c2ecf20Sopenharmony_ci break; 16928c2ecf20Sopenharmony_ci case HTC_RECORD_LOOKAHEAD: 16938c2ecf20Sopenharmony_ci len = record->len / sizeof(*lk_ahd); 16948c2ecf20Sopenharmony_ci if (!len) { 16958c2ecf20Sopenharmony_ci WARN_ON(1); 16968c2ecf20Sopenharmony_ci return -EINVAL; 16978c2ecf20Sopenharmony_ci } 16988c2ecf20Sopenharmony_ci 16998c2ecf20Sopenharmony_ci lk_ahd = (struct htc_lookahead_report *) record_buf; 17008c2ecf20Sopenharmony_ci if ((lk_ahd->pre_valid == ((~lk_ahd->post_valid) & 0xFF)) && 17018c2ecf20Sopenharmony_ci next_lk_ahds) { 17028c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 17038c2ecf20Sopenharmony_ci "htc rx lk_ahd found pre_valid 0x%x post_valid 0x%x\n", 17048c2ecf20Sopenharmony_ci lk_ahd->pre_valid, lk_ahd->post_valid); 17058c2ecf20Sopenharmony_ci 17068c2ecf20Sopenharmony_ci /* look ahead bytes are valid, copy them over */ 17078c2ecf20Sopenharmony_ci memcpy((u8 *)&next_lk_ahds[0], lk_ahd->lk_ahd, 4); 17088c2ecf20Sopenharmony_ci 17098c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, 17108c2ecf20Sopenharmony_ci "htc rx next look ahead", 17118c2ecf20Sopenharmony_ci "", next_lk_ahds, 4); 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci *n_lk_ahds = 1; 17148c2ecf20Sopenharmony_ci } 17158c2ecf20Sopenharmony_ci break; 17168c2ecf20Sopenharmony_ci case HTC_RECORD_LOOKAHEAD_BUNDLE: 17178c2ecf20Sopenharmony_ci len = record->len / sizeof(*bundle_lkahd_rpt); 17188c2ecf20Sopenharmony_ci if (!len || (len > HTC_HOST_MAX_MSG_PER_BUNDLE)) { 17198c2ecf20Sopenharmony_ci WARN_ON(1); 17208c2ecf20Sopenharmony_ci return -EINVAL; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci if (next_lk_ahds) { 17248c2ecf20Sopenharmony_ci int i; 17258c2ecf20Sopenharmony_ci 17268c2ecf20Sopenharmony_ci bundle_lkahd_rpt = 17278c2ecf20Sopenharmony_ci (struct htc_bundle_lkahd_rpt *) record_buf; 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bundle lk_ahd", 17308c2ecf20Sopenharmony_ci "", record_buf, record->len); 17318c2ecf20Sopenharmony_ci 17328c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) { 17338c2ecf20Sopenharmony_ci memcpy((u8 *)&next_lk_ahds[i], 17348c2ecf20Sopenharmony_ci bundle_lkahd_rpt->lk_ahd, 4); 17358c2ecf20Sopenharmony_ci bundle_lkahd_rpt++; 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci *n_lk_ahds = i; 17398c2ecf20Sopenharmony_ci } 17408c2ecf20Sopenharmony_ci break; 17418c2ecf20Sopenharmony_ci default: 17428c2ecf20Sopenharmony_ci ath6kl_err("unhandled record: id:%d len:%d\n", 17438c2ecf20Sopenharmony_ci record->rec_id, record->len); 17448c2ecf20Sopenharmony_ci break; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci return 0; 17488c2ecf20Sopenharmony_ci} 17498c2ecf20Sopenharmony_ci 17508c2ecf20Sopenharmony_cistatic int htc_proc_trailer(struct htc_target *target, 17518c2ecf20Sopenharmony_ci u8 *buf, int len, u32 *next_lk_ahds, 17528c2ecf20Sopenharmony_ci int *n_lk_ahds, enum htc_endpoint_id endpoint) 17538c2ecf20Sopenharmony_ci{ 17548c2ecf20Sopenharmony_ci struct htc_record_hdr *record; 17558c2ecf20Sopenharmony_ci int orig_len; 17568c2ecf20Sopenharmony_ci int status; 17578c2ecf20Sopenharmony_ci u8 *record_buf; 17588c2ecf20Sopenharmony_ci u8 *orig_buf; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, "htc rx trailer len %d\n", len); 17618c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, NULL, "", buf, len); 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci orig_buf = buf; 17648c2ecf20Sopenharmony_ci orig_len = len; 17658c2ecf20Sopenharmony_ci status = 0; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci while (len > 0) { 17688c2ecf20Sopenharmony_ci if (len < sizeof(struct htc_record_hdr)) { 17698c2ecf20Sopenharmony_ci status = -ENOMEM; 17708c2ecf20Sopenharmony_ci break; 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci /* these are byte aligned structs */ 17738c2ecf20Sopenharmony_ci record = (struct htc_record_hdr *) buf; 17748c2ecf20Sopenharmony_ci len -= sizeof(struct htc_record_hdr); 17758c2ecf20Sopenharmony_ci buf += sizeof(struct htc_record_hdr); 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (record->len > len) { 17788c2ecf20Sopenharmony_ci ath6kl_err("invalid record len: %d (id:%d) buf has: %d bytes left\n", 17798c2ecf20Sopenharmony_ci record->len, record->rec_id, len); 17808c2ecf20Sopenharmony_ci status = -ENOMEM; 17818c2ecf20Sopenharmony_ci break; 17828c2ecf20Sopenharmony_ci } 17838c2ecf20Sopenharmony_ci record_buf = buf; 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci status = htc_parse_trailer(target, record, record_buf, 17868c2ecf20Sopenharmony_ci next_lk_ahds, endpoint, n_lk_ahds); 17878c2ecf20Sopenharmony_ci 17888c2ecf20Sopenharmony_ci if (status) 17898c2ecf20Sopenharmony_ci break; 17908c2ecf20Sopenharmony_ci 17918c2ecf20Sopenharmony_ci /* advance buffer past this record for next time around */ 17928c2ecf20Sopenharmony_ci buf += record->len; 17938c2ecf20Sopenharmony_ci len -= record->len; 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci 17968c2ecf20Sopenharmony_ci if (status) 17978c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad trailer", 17988c2ecf20Sopenharmony_ci "", orig_buf, orig_len); 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci return status; 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_process_hdr(struct htc_target *target, 18048c2ecf20Sopenharmony_ci struct htc_packet *packet, 18058c2ecf20Sopenharmony_ci u32 *next_lkahds, int *n_lkahds) 18068c2ecf20Sopenharmony_ci{ 18078c2ecf20Sopenharmony_ci int status = 0; 18088c2ecf20Sopenharmony_ci u16 payload_len; 18098c2ecf20Sopenharmony_ci u32 lk_ahd; 18108c2ecf20Sopenharmony_ci struct htc_frame_hdr *htc_hdr = (struct htc_frame_hdr *)packet->buf; 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_ci if (n_lkahds != NULL) 18138c2ecf20Sopenharmony_ci *n_lkahds = 0; 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_ci /* 18168c2ecf20Sopenharmony_ci * NOTE: we cannot assume the alignment of buf, so we use the safe 18178c2ecf20Sopenharmony_ci * macros to retrieve 16 bit fields. 18188c2ecf20Sopenharmony_ci */ 18198c2ecf20Sopenharmony_ci payload_len = le16_to_cpu(get_unaligned(&htc_hdr->payld_len)); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci memcpy((u8 *)&lk_ahd, packet->buf, sizeof(lk_ahd)); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_ci if (packet->info.rx.rx_flags & HTC_RX_PKT_REFRESH_HDR) { 18248c2ecf20Sopenharmony_ci /* 18258c2ecf20Sopenharmony_ci * Refresh the expected header and the actual length as it 18268c2ecf20Sopenharmony_ci * was unknown when this packet was grabbed as part of the 18278c2ecf20Sopenharmony_ci * bundle. 18288c2ecf20Sopenharmony_ci */ 18298c2ecf20Sopenharmony_ci packet->info.rx.exp_hdr = lk_ahd; 18308c2ecf20Sopenharmony_ci packet->act_len = payload_len + HTC_HDR_LENGTH; 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci /* validate the actual header that was refreshed */ 18338c2ecf20Sopenharmony_ci if (packet->act_len > packet->buf_len) { 18348c2ecf20Sopenharmony_ci ath6kl_err("refreshed hdr payload len (%d) in bundled recv is invalid (hdr: 0x%X)\n", 18358c2ecf20Sopenharmony_ci payload_len, lk_ahd); 18368c2ecf20Sopenharmony_ci /* 18378c2ecf20Sopenharmony_ci * Limit this to max buffer just to print out some 18388c2ecf20Sopenharmony_ci * of the buffer. 18398c2ecf20Sopenharmony_ci */ 18408c2ecf20Sopenharmony_ci packet->act_len = min(packet->act_len, packet->buf_len); 18418c2ecf20Sopenharmony_ci status = -ENOMEM; 18428c2ecf20Sopenharmony_ci goto fail_rx; 18438c2ecf20Sopenharmony_ci } 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci if (packet->endpoint != htc_hdr->eid) { 18468c2ecf20Sopenharmony_ci ath6kl_err("refreshed hdr ep (%d) does not match expected ep (%d)\n", 18478c2ecf20Sopenharmony_ci htc_hdr->eid, packet->endpoint); 18488c2ecf20Sopenharmony_ci status = -ENOMEM; 18498c2ecf20Sopenharmony_ci goto fail_rx; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci } 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci if (lk_ahd != packet->info.rx.exp_hdr) { 18548c2ecf20Sopenharmony_ci ath6kl_err("%s(): lk_ahd mismatch! (pPkt:0x%p flags:0x%X)\n", 18558c2ecf20Sopenharmony_ci __func__, packet, packet->info.rx.rx_flags); 18568c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx expected lk_ahd", 18578c2ecf20Sopenharmony_ci "", &packet->info.rx.exp_hdr, 4); 18588c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx current header", 18598c2ecf20Sopenharmony_ci "", (u8 *)&lk_ahd, sizeof(lk_ahd)); 18608c2ecf20Sopenharmony_ci status = -ENOMEM; 18618c2ecf20Sopenharmony_ci goto fail_rx; 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (htc_hdr->flags & HTC_FLG_RX_TRAILER) { 18658c2ecf20Sopenharmony_ci if (htc_hdr->ctrl[0] < sizeof(struct htc_record_hdr) || 18668c2ecf20Sopenharmony_ci htc_hdr->ctrl[0] > payload_len) { 18678c2ecf20Sopenharmony_ci ath6kl_err("%s(): invalid hdr (payload len should be :%d, CB[0] is:%d)\n", 18688c2ecf20Sopenharmony_ci __func__, payload_len, htc_hdr->ctrl[0]); 18698c2ecf20Sopenharmony_ci status = -ENOMEM; 18708c2ecf20Sopenharmony_ci goto fail_rx; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci if (packet->info.rx.rx_flags & HTC_RX_PKT_IGNORE_LOOKAHEAD) { 18748c2ecf20Sopenharmony_ci next_lkahds = NULL; 18758c2ecf20Sopenharmony_ci n_lkahds = NULL; 18768c2ecf20Sopenharmony_ci } 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci status = htc_proc_trailer(target, packet->buf + HTC_HDR_LENGTH 18798c2ecf20Sopenharmony_ci + payload_len - htc_hdr->ctrl[0], 18808c2ecf20Sopenharmony_ci htc_hdr->ctrl[0], next_lkahds, 18818c2ecf20Sopenharmony_ci n_lkahds, packet->endpoint); 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci if (status) 18848c2ecf20Sopenharmony_ci goto fail_rx; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci packet->act_len -= htc_hdr->ctrl[0]; 18878c2ecf20Sopenharmony_ci } 18888c2ecf20Sopenharmony_ci 18898c2ecf20Sopenharmony_ci packet->buf += HTC_HDR_LENGTH; 18908c2ecf20Sopenharmony_ci packet->act_len -= HTC_HDR_LENGTH; 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_cifail_rx: 18938c2ecf20Sopenharmony_ci if (status) 18948c2ecf20Sopenharmony_ci ath6kl_dbg_dump(ATH6KL_DBG_HTC, "htc rx bad packet", 18958c2ecf20Sopenharmony_ci "", packet->buf, packet->act_len); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci return status; 18988c2ecf20Sopenharmony_ci} 18998c2ecf20Sopenharmony_ci 19008c2ecf20Sopenharmony_cistatic void ath6kl_htc_rx_complete(struct htc_endpoint *endpoint, 19018c2ecf20Sopenharmony_ci struct htc_packet *packet) 19028c2ecf20Sopenharmony_ci{ 19038c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 19048c2ecf20Sopenharmony_ci "htc rx complete ep %d packet 0x%p\n", 19058c2ecf20Sopenharmony_ci endpoint->eid, packet); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci endpoint->ep_cb.rx(endpoint->target, packet); 19088c2ecf20Sopenharmony_ci} 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_bundle(struct htc_target *target, 19118c2ecf20Sopenharmony_ci struct list_head *rxq, 19128c2ecf20Sopenharmony_ci struct list_head *sync_compq, 19138c2ecf20Sopenharmony_ci int *n_pkt_fetched, bool part_bundle) 19148c2ecf20Sopenharmony_ci{ 19158c2ecf20Sopenharmony_ci struct hif_scatter_req *scat_req; 19168c2ecf20Sopenharmony_ci struct htc_packet *packet; 19178c2ecf20Sopenharmony_ci int rem_space = target->max_rx_bndl_sz; 19188c2ecf20Sopenharmony_ci int n_scat_pkt, status = 0, i, len; 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_ci n_scat_pkt = get_queue_depth(rxq); 19218c2ecf20Sopenharmony_ci n_scat_pkt = min(n_scat_pkt, target->msg_per_bndl_max); 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_ci if ((get_queue_depth(rxq) - n_scat_pkt) > 0) { 19248c2ecf20Sopenharmony_ci /* 19258c2ecf20Sopenharmony_ci * We were forced to split this bundle receive operation 19268c2ecf20Sopenharmony_ci * all packets in this partial bundle must have their 19278c2ecf20Sopenharmony_ci * lookaheads ignored. 19288c2ecf20Sopenharmony_ci */ 19298c2ecf20Sopenharmony_ci part_bundle = true; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci /* 19328c2ecf20Sopenharmony_ci * This would only happen if the target ignored our max 19338c2ecf20Sopenharmony_ci * bundle limit. 19348c2ecf20Sopenharmony_ci */ 19358c2ecf20Sopenharmony_ci ath6kl_warn("%s(): partial bundle detected num:%d , %d\n", 19368c2ecf20Sopenharmony_ci __func__, get_queue_depth(rxq), n_scat_pkt); 19378c2ecf20Sopenharmony_ci } 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci len = 0; 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 19428c2ecf20Sopenharmony_ci "htc rx bundle depth %d pkts %d\n", 19438c2ecf20Sopenharmony_ci get_queue_depth(rxq), n_scat_pkt); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ci scat_req = hif_scatter_req_get(target->dev->ar); 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci if (scat_req == NULL) 19488c2ecf20Sopenharmony_ci goto fail_rx_pkt; 19498c2ecf20Sopenharmony_ci 19508c2ecf20Sopenharmony_ci for (i = 0; i < n_scat_pkt; i++) { 19518c2ecf20Sopenharmony_ci int pad_len; 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci packet = list_first_entry(rxq, struct htc_packet, list); 19548c2ecf20Sopenharmony_ci list_del(&packet->list); 19558c2ecf20Sopenharmony_ci 19568c2ecf20Sopenharmony_ci pad_len = CALC_TXRX_PADDED_LEN(target, 19578c2ecf20Sopenharmony_ci packet->act_len); 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci if ((rem_space - pad_len) < 0) { 19608c2ecf20Sopenharmony_ci list_add(&packet->list, rxq); 19618c2ecf20Sopenharmony_ci break; 19628c2ecf20Sopenharmony_ci } 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci rem_space -= pad_len; 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci if (part_bundle || (i < (n_scat_pkt - 1))) 19678c2ecf20Sopenharmony_ci /* 19688c2ecf20Sopenharmony_ci * Packet 0..n-1 cannot be checked for look-aheads 19698c2ecf20Sopenharmony_ci * since we are fetching a bundle the last packet 19708c2ecf20Sopenharmony_ci * however can have it's lookahead used 19718c2ecf20Sopenharmony_ci */ 19728c2ecf20Sopenharmony_ci packet->info.rx.rx_flags |= 19738c2ecf20Sopenharmony_ci HTC_RX_PKT_IGNORE_LOOKAHEAD; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* NOTE: 1 HTC packet per scatter entry */ 19768c2ecf20Sopenharmony_ci scat_req->scat_list[i].buf = packet->buf; 19778c2ecf20Sopenharmony_ci scat_req->scat_list[i].len = pad_len; 19788c2ecf20Sopenharmony_ci 19798c2ecf20Sopenharmony_ci packet->info.rx.rx_flags |= HTC_RX_PKT_PART_OF_BUNDLE; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci list_add_tail(&packet->list, sync_compq); 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci WARN_ON(!scat_req->scat_list[i].len); 19848c2ecf20Sopenharmony_ci len += scat_req->scat_list[i].len; 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci scat_req->len = len; 19888c2ecf20Sopenharmony_ci scat_req->scat_entries = i; 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci status = ath6kl_hif_submit_scat_req(target->dev, scat_req, true); 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (!status) 19938c2ecf20Sopenharmony_ci *n_pkt_fetched = i; 19948c2ecf20Sopenharmony_ci 19958c2ecf20Sopenharmony_ci /* free scatter request */ 19968c2ecf20Sopenharmony_ci hif_scatter_req_add(target->dev->ar, scat_req); 19978c2ecf20Sopenharmony_ci 19988c2ecf20Sopenharmony_cifail_rx_pkt: 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_ci return status; 20018c2ecf20Sopenharmony_ci} 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_process_packets(struct htc_target *target, 20048c2ecf20Sopenharmony_ci struct list_head *comp_pktq, 20058c2ecf20Sopenharmony_ci u32 lk_ahds[], 20068c2ecf20Sopenharmony_ci int *n_lk_ahd) 20078c2ecf20Sopenharmony_ci{ 20088c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 20098c2ecf20Sopenharmony_ci struct htc_endpoint *ep; 20108c2ecf20Sopenharmony_ci int status = 0; 20118c2ecf20Sopenharmony_ci 20128c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, comp_pktq, list) { 20138c2ecf20Sopenharmony_ci ep = &target->endpoint[packet->endpoint]; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci trace_ath6kl_htc_rx(packet->status, packet->endpoint, 20168c2ecf20Sopenharmony_ci packet->buf, packet->act_len); 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci /* process header for each of the recv packet */ 20198c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_process_hdr(target, packet, lk_ahds, 20208c2ecf20Sopenharmony_ci n_lk_ahd); 20218c2ecf20Sopenharmony_ci if (status) 20228c2ecf20Sopenharmony_ci return status; 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci list_del(&packet->list); 20258c2ecf20Sopenharmony_ci 20268c2ecf20Sopenharmony_ci if (list_empty(comp_pktq)) { 20278c2ecf20Sopenharmony_ci /* 20288c2ecf20Sopenharmony_ci * Last packet's more packet flag is set 20298c2ecf20Sopenharmony_ci * based on the lookahead. 20308c2ecf20Sopenharmony_ci */ 20318c2ecf20Sopenharmony_ci if (*n_lk_ahd > 0) 20328c2ecf20Sopenharmony_ci ath6kl_htc_rx_set_indicate(lk_ahds[0], 20338c2ecf20Sopenharmony_ci ep, packet); 20348c2ecf20Sopenharmony_ci } else 20358c2ecf20Sopenharmony_ci /* 20368c2ecf20Sopenharmony_ci * Packets in a bundle automatically have 20378c2ecf20Sopenharmony_ci * this flag set. 20388c2ecf20Sopenharmony_ci */ 20398c2ecf20Sopenharmony_ci packet->info.rx.indicat_flags |= 20408c2ecf20Sopenharmony_ci HTC_RX_FLAGS_INDICATE_MORE_PKTS; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci ath6kl_htc_rx_update_stats(ep, *n_lk_ahd); 20438c2ecf20Sopenharmony_ci 20448c2ecf20Sopenharmony_ci if (packet->info.rx.rx_flags & HTC_RX_PKT_PART_OF_BUNDLE) 20458c2ecf20Sopenharmony_ci ep->ep_st.rx_bundl += 1; 20468c2ecf20Sopenharmony_ci 20478c2ecf20Sopenharmony_ci ath6kl_htc_rx_complete(ep, packet); 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci return status; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_cistatic int ath6kl_htc_rx_fetch(struct htc_target *target, 20548c2ecf20Sopenharmony_ci struct list_head *rx_pktq, 20558c2ecf20Sopenharmony_ci struct list_head *comp_pktq) 20568c2ecf20Sopenharmony_ci{ 20578c2ecf20Sopenharmony_ci int fetched_pkts; 20588c2ecf20Sopenharmony_ci bool part_bundle = false; 20598c2ecf20Sopenharmony_ci int status = 0; 20608c2ecf20Sopenharmony_ci struct list_head tmp_rxq; 20618c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci /* now go fetch the list of HTC packets */ 20648c2ecf20Sopenharmony_ci while (!list_empty(rx_pktq)) { 20658c2ecf20Sopenharmony_ci fetched_pkts = 0; 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&tmp_rxq); 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci if (target->rx_bndl_enable && (get_queue_depth(rx_pktq) > 1)) { 20708c2ecf20Sopenharmony_ci /* 20718c2ecf20Sopenharmony_ci * There are enough packets to attempt a 20728c2ecf20Sopenharmony_ci * bundle transfer and recv bundling is 20738c2ecf20Sopenharmony_ci * allowed. 20748c2ecf20Sopenharmony_ci */ 20758c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_bundle(target, rx_pktq, 20768c2ecf20Sopenharmony_ci &tmp_rxq, 20778c2ecf20Sopenharmony_ci &fetched_pkts, 20788c2ecf20Sopenharmony_ci part_bundle); 20798c2ecf20Sopenharmony_ci if (status) 20808c2ecf20Sopenharmony_ci goto fail_rx; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci if (!list_empty(rx_pktq)) 20838c2ecf20Sopenharmony_ci part_bundle = true; 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci list_splice_tail_init(&tmp_rxq, comp_pktq); 20868c2ecf20Sopenharmony_ci } 20878c2ecf20Sopenharmony_ci 20888c2ecf20Sopenharmony_ci if (!fetched_pkts) { 20898c2ecf20Sopenharmony_ci packet = list_first_entry(rx_pktq, struct htc_packet, 20908c2ecf20Sopenharmony_ci list); 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci /* fully synchronous */ 20938c2ecf20Sopenharmony_ci packet->completion = NULL; 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci if (!list_is_singular(rx_pktq)) 20968c2ecf20Sopenharmony_ci /* 20978c2ecf20Sopenharmony_ci * look_aheads in all packet 20988c2ecf20Sopenharmony_ci * except the last one in the 20998c2ecf20Sopenharmony_ci * bundle must be ignored 21008c2ecf20Sopenharmony_ci */ 21018c2ecf20Sopenharmony_ci packet->info.rx.rx_flags |= 21028c2ecf20Sopenharmony_ci HTC_RX_PKT_IGNORE_LOOKAHEAD; 21038c2ecf20Sopenharmony_ci 21048c2ecf20Sopenharmony_ci /* go fetch the packet */ 21058c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_packet(target, packet, 21068c2ecf20Sopenharmony_ci packet->act_len); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci list_move_tail(&packet->list, &tmp_rxq); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if (status) 21118c2ecf20Sopenharmony_ci goto fail_rx; 21128c2ecf20Sopenharmony_ci 21138c2ecf20Sopenharmony_ci list_splice_tail_init(&tmp_rxq, comp_pktq); 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci } 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci return 0; 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_cifail_rx: 21208c2ecf20Sopenharmony_ci 21218c2ecf20Sopenharmony_ci /* 21228c2ecf20Sopenharmony_ci * Cleanup any packets we allocated but didn't use to 21238c2ecf20Sopenharmony_ci * actually fetch any packets. 21248c2ecf20Sopenharmony_ci */ 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, rx_pktq, list) { 21278c2ecf20Sopenharmony_ci list_del(&packet->list); 21288c2ecf20Sopenharmony_ci htc_reclaim_rxbuf(target, packet, 21298c2ecf20Sopenharmony_ci &target->endpoint[packet->endpoint]); 21308c2ecf20Sopenharmony_ci } 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, &tmp_rxq, list) { 21338c2ecf20Sopenharmony_ci list_del(&packet->list); 21348c2ecf20Sopenharmony_ci htc_reclaim_rxbuf(target, packet, 21358c2ecf20Sopenharmony_ci &target->endpoint[packet->endpoint]); 21368c2ecf20Sopenharmony_ci } 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci return status; 21398c2ecf20Sopenharmony_ci} 21408c2ecf20Sopenharmony_ci 21418c2ecf20Sopenharmony_ciint ath6kl_htc_rxmsg_pending_handler(struct htc_target *target, 21428c2ecf20Sopenharmony_ci u32 msg_look_ahead, int *num_pkts) 21438c2ecf20Sopenharmony_ci{ 21448c2ecf20Sopenharmony_ci struct htc_packet *packets, *tmp_pkt; 21458c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 21468c2ecf20Sopenharmony_ci struct list_head rx_pktq, comp_pktq; 21478c2ecf20Sopenharmony_ci int status = 0; 21488c2ecf20Sopenharmony_ci u32 look_aheads[HTC_HOST_MAX_MSG_PER_BUNDLE]; 21498c2ecf20Sopenharmony_ci int num_look_ahead = 1; 21508c2ecf20Sopenharmony_ci enum htc_endpoint_id id; 21518c2ecf20Sopenharmony_ci int n_fetched = 0; 21528c2ecf20Sopenharmony_ci 21538c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&comp_pktq); 21548c2ecf20Sopenharmony_ci *num_pkts = 0; 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_ci /* 21578c2ecf20Sopenharmony_ci * On first entry copy the look_aheads into our temp array for 21588c2ecf20Sopenharmony_ci * processing 21598c2ecf20Sopenharmony_ci */ 21608c2ecf20Sopenharmony_ci look_aheads[0] = msg_look_ahead; 21618c2ecf20Sopenharmony_ci 21628c2ecf20Sopenharmony_ci while (true) { 21638c2ecf20Sopenharmony_ci /* 21648c2ecf20Sopenharmony_ci * First lookahead sets the expected endpoint IDs for all 21658c2ecf20Sopenharmony_ci * packets in a bundle. 21668c2ecf20Sopenharmony_ci */ 21678c2ecf20Sopenharmony_ci id = ((struct htc_frame_hdr *)&look_aheads[0])->eid; 21688c2ecf20Sopenharmony_ci endpoint = &target->endpoint[id]; 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_ci if (id >= ENDPOINT_MAX) { 21718c2ecf20Sopenharmony_ci ath6kl_err("MsgPend, invalid endpoint in look-ahead: %d\n", 21728c2ecf20Sopenharmony_ci id); 21738c2ecf20Sopenharmony_ci status = -ENOMEM; 21748c2ecf20Sopenharmony_ci break; 21758c2ecf20Sopenharmony_ci } 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&rx_pktq); 21788c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&comp_pktq); 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci /* 21818c2ecf20Sopenharmony_ci * Try to allocate as many HTC RX packets indicated by the 21828c2ecf20Sopenharmony_ci * look_aheads. 21838c2ecf20Sopenharmony_ci */ 21848c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_alloc(target, look_aheads, 21858c2ecf20Sopenharmony_ci num_look_ahead, endpoint, 21868c2ecf20Sopenharmony_ci &rx_pktq); 21878c2ecf20Sopenharmony_ci if (status) 21888c2ecf20Sopenharmony_ci break; 21898c2ecf20Sopenharmony_ci 21908c2ecf20Sopenharmony_ci if (get_queue_depth(&rx_pktq) >= 2) 21918c2ecf20Sopenharmony_ci /* 21928c2ecf20Sopenharmony_ci * A recv bundle was detected, force IRQ status 21938c2ecf20Sopenharmony_ci * re-check again 21948c2ecf20Sopenharmony_ci */ 21958c2ecf20Sopenharmony_ci target->chk_irq_status_cnt = 1; 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci n_fetched += get_queue_depth(&rx_pktq); 21988c2ecf20Sopenharmony_ci 21998c2ecf20Sopenharmony_ci num_look_ahead = 0; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_fetch(target, &rx_pktq, &comp_pktq); 22028c2ecf20Sopenharmony_ci 22038c2ecf20Sopenharmony_ci if (!status) 22048c2ecf20Sopenharmony_ci ath6kl_htc_rx_chk_water_mark(endpoint); 22058c2ecf20Sopenharmony_ci 22068c2ecf20Sopenharmony_ci /* Process fetched packets */ 22078c2ecf20Sopenharmony_ci status = ath6kl_htc_rx_process_packets(target, &comp_pktq, 22088c2ecf20Sopenharmony_ci look_aheads, 22098c2ecf20Sopenharmony_ci &num_look_ahead); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci if (!num_look_ahead || status) 22128c2ecf20Sopenharmony_ci break; 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci /* 22158c2ecf20Sopenharmony_ci * For SYNCH processing, if we get here, we are running 22168c2ecf20Sopenharmony_ci * through the loop again due to a detected lookahead. Set 22178c2ecf20Sopenharmony_ci * flag that we should re-check IRQ status registers again 22188c2ecf20Sopenharmony_ci * before leaving IRQ processing, this can net better 22198c2ecf20Sopenharmony_ci * performance in high throughput situations. 22208c2ecf20Sopenharmony_ci */ 22218c2ecf20Sopenharmony_ci target->chk_irq_status_cnt = 1; 22228c2ecf20Sopenharmony_ci } 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci if (status) { 22258c2ecf20Sopenharmony_ci if (status != -ECANCELED) 22268c2ecf20Sopenharmony_ci ath6kl_err("failed to get pending recv messages: %d\n", 22278c2ecf20Sopenharmony_ci status); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_ci /* cleanup any packets in sync completion queue */ 22308c2ecf20Sopenharmony_ci list_for_each_entry_safe(packets, tmp_pkt, &comp_pktq, list) { 22318c2ecf20Sopenharmony_ci list_del(&packets->list); 22328c2ecf20Sopenharmony_ci htc_reclaim_rxbuf(target, packets, 22338c2ecf20Sopenharmony_ci &target->endpoint[packets->endpoint]); 22348c2ecf20Sopenharmony_ci } 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci if (target->htc_flags & HTC_OP_STATE_STOPPING) { 22378c2ecf20Sopenharmony_ci ath6kl_warn("host is going to stop blocking receiver for htc_stop\n"); 22388c2ecf20Sopenharmony_ci ath6kl_hif_rx_control(target->dev, false); 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci /* 22438c2ecf20Sopenharmony_ci * Before leaving, check to see if host ran out of buffers and 22448c2ecf20Sopenharmony_ci * needs to stop the receiver. 22458c2ecf20Sopenharmony_ci */ 22468c2ecf20Sopenharmony_ci if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { 22478c2ecf20Sopenharmony_ci ath6kl_warn("host has no rx buffers blocking receiver to prevent overrun\n"); 22488c2ecf20Sopenharmony_ci ath6kl_hif_rx_control(target->dev, false); 22498c2ecf20Sopenharmony_ci } 22508c2ecf20Sopenharmony_ci *num_pkts = n_fetched; 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci return status; 22538c2ecf20Sopenharmony_ci} 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci/* 22568c2ecf20Sopenharmony_ci * Synchronously wait for a control message from the target, 22578c2ecf20Sopenharmony_ci * This function is used at initialization time ONLY. At init messages 22588c2ecf20Sopenharmony_ci * on ENDPOINT 0 are expected. 22598c2ecf20Sopenharmony_ci */ 22608c2ecf20Sopenharmony_cistatic struct htc_packet *htc_wait_for_ctrl_msg(struct htc_target *target) 22618c2ecf20Sopenharmony_ci{ 22628c2ecf20Sopenharmony_ci struct htc_packet *packet = NULL; 22638c2ecf20Sopenharmony_ci struct htc_frame_look_ahead look_ahead; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci if (ath6kl_hif_poll_mboxmsg_rx(target->dev, &look_ahead.word, 22668c2ecf20Sopenharmony_ci HTC_TARGET_RESPONSE_TIMEOUT)) 22678c2ecf20Sopenharmony_ci return NULL; 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 22708c2ecf20Sopenharmony_ci "htc rx wait ctrl look_ahead 0x%X\n", look_ahead.word); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci if (look_ahead.eid != ENDPOINT_0) 22738c2ecf20Sopenharmony_ci return NULL; 22748c2ecf20Sopenharmony_ci 22758c2ecf20Sopenharmony_ci packet = htc_get_control_buf(target, false); 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci if (!packet) 22788c2ecf20Sopenharmony_ci return NULL; 22798c2ecf20Sopenharmony_ci 22808c2ecf20Sopenharmony_ci packet->info.rx.rx_flags = 0; 22818c2ecf20Sopenharmony_ci packet->info.rx.exp_hdr = look_ahead.word; 22828c2ecf20Sopenharmony_ci packet->act_len = le16_to_cpu(look_ahead.payld_len) + HTC_HDR_LENGTH; 22838c2ecf20Sopenharmony_ci 22848c2ecf20Sopenharmony_ci if (packet->act_len > packet->buf_len) 22858c2ecf20Sopenharmony_ci goto fail_ctrl_rx; 22868c2ecf20Sopenharmony_ci 22878c2ecf20Sopenharmony_ci /* we want synchronous operation */ 22888c2ecf20Sopenharmony_ci packet->completion = NULL; 22898c2ecf20Sopenharmony_ci 22908c2ecf20Sopenharmony_ci /* get the message from the device, this will block */ 22918c2ecf20Sopenharmony_ci if (ath6kl_htc_rx_packet(target, packet, packet->act_len)) 22928c2ecf20Sopenharmony_ci goto fail_ctrl_rx; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci trace_ath6kl_htc_rx(packet->status, packet->endpoint, 22958c2ecf20Sopenharmony_ci packet->buf, packet->act_len); 22968c2ecf20Sopenharmony_ci 22978c2ecf20Sopenharmony_ci /* process receive header */ 22988c2ecf20Sopenharmony_ci packet->status = ath6kl_htc_rx_process_hdr(target, packet, NULL, NULL); 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci if (packet->status) { 23018c2ecf20Sopenharmony_ci ath6kl_err("htc_wait_for_ctrl_msg, ath6kl_htc_rx_process_hdr failed (status = %d)\n", 23028c2ecf20Sopenharmony_ci packet->status); 23038c2ecf20Sopenharmony_ci goto fail_ctrl_rx; 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci return packet; 23078c2ecf20Sopenharmony_ci 23088c2ecf20Sopenharmony_cifail_ctrl_rx: 23098c2ecf20Sopenharmony_ci if (packet != NULL) { 23108c2ecf20Sopenharmony_ci htc_rxpkt_reset(packet); 23118c2ecf20Sopenharmony_ci reclaim_rx_ctrl_buf(target, packet); 23128c2ecf20Sopenharmony_ci } 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci return NULL; 23158c2ecf20Sopenharmony_ci} 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_add_rxbuf_multiple(struct htc_target *target, 23188c2ecf20Sopenharmony_ci struct list_head *pkt_queue) 23198c2ecf20Sopenharmony_ci{ 23208c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 23218c2ecf20Sopenharmony_ci struct htc_packet *first_pkt; 23228c2ecf20Sopenharmony_ci bool rx_unblock = false; 23238c2ecf20Sopenharmony_ci int status = 0, depth; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci if (list_empty(pkt_queue)) 23268c2ecf20Sopenharmony_ci return -ENOMEM; 23278c2ecf20Sopenharmony_ci 23288c2ecf20Sopenharmony_ci first_pkt = list_first_entry(pkt_queue, struct htc_packet, list); 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci if (first_pkt->endpoint >= ENDPOINT_MAX) 23318c2ecf20Sopenharmony_ci return status; 23328c2ecf20Sopenharmony_ci 23338c2ecf20Sopenharmony_ci depth = get_queue_depth(pkt_queue); 23348c2ecf20Sopenharmony_ci 23358c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 23368c2ecf20Sopenharmony_ci "htc rx add multiple ep id %d cnt %d len %d\n", 23378c2ecf20Sopenharmony_ci first_pkt->endpoint, depth, first_pkt->buf_len); 23388c2ecf20Sopenharmony_ci 23398c2ecf20Sopenharmony_ci endpoint = &target->endpoint[first_pkt->endpoint]; 23408c2ecf20Sopenharmony_ci 23418c2ecf20Sopenharmony_ci if (target->htc_flags & HTC_OP_STATE_STOPPING) { 23428c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci /* walk through queue and mark each one canceled */ 23458c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, pkt_queue, list) { 23468c2ecf20Sopenharmony_ci packet->status = -ECANCELED; 23478c2ecf20Sopenharmony_ci list_del(&packet->list); 23488c2ecf20Sopenharmony_ci ath6kl_htc_rx_complete(endpoint, packet); 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci 23518c2ecf20Sopenharmony_ci return status; 23528c2ecf20Sopenharmony_ci } 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_ci list_splice_tail_init(pkt_queue, &endpoint->rx_bufq); 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci /* check if we are blocked waiting for a new buffer */ 23598c2ecf20Sopenharmony_ci if (target->rx_st_flags & HTC_RECV_WAIT_BUFFERS) { 23608c2ecf20Sopenharmony_ci if (target->ep_waiting == first_pkt->endpoint) { 23618c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 23628c2ecf20Sopenharmony_ci "htc rx blocked on ep %d, unblocking\n", 23638c2ecf20Sopenharmony_ci target->ep_waiting); 23648c2ecf20Sopenharmony_ci target->rx_st_flags &= ~HTC_RECV_WAIT_BUFFERS; 23658c2ecf20Sopenharmony_ci target->ep_waiting = ENDPOINT_MAX; 23668c2ecf20Sopenharmony_ci rx_unblock = true; 23678c2ecf20Sopenharmony_ci } 23688c2ecf20Sopenharmony_ci } 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 23718c2ecf20Sopenharmony_ci 23728c2ecf20Sopenharmony_ci if (rx_unblock && !(target->htc_flags & HTC_OP_STATE_STOPPING)) 23738c2ecf20Sopenharmony_ci /* TODO : implement a buffer threshold count? */ 23748c2ecf20Sopenharmony_ci ath6kl_hif_rx_control(target->dev, true); 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci return status; 23778c2ecf20Sopenharmony_ci} 23788c2ecf20Sopenharmony_ci 23798c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_flush_rx_buf(struct htc_target *target) 23808c2ecf20Sopenharmony_ci{ 23818c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 23828c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_pkt; 23838c2ecf20Sopenharmony_ci int i; 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { 23868c2ecf20Sopenharmony_ci endpoint = &target->endpoint[i]; 23878c2ecf20Sopenharmony_ci if (!endpoint->svc_id) 23888c2ecf20Sopenharmony_ci /* not in use.. */ 23898c2ecf20Sopenharmony_ci continue; 23908c2ecf20Sopenharmony_ci 23918c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 23928c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_pkt, 23938c2ecf20Sopenharmony_ci &endpoint->rx_bufq, list) { 23948c2ecf20Sopenharmony_ci list_del(&packet->list); 23958c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 23968c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 23978c2ecf20Sopenharmony_ci "htc rx flush pkt 0x%p len %d ep %d\n", 23988c2ecf20Sopenharmony_ci packet, packet->buf_len, 23998c2ecf20Sopenharmony_ci packet->endpoint); 24008c2ecf20Sopenharmony_ci /* 24018c2ecf20Sopenharmony_ci * packets in rx_bufq of endpoint 0 have originally 24028c2ecf20Sopenharmony_ci * been queued from target->free_ctrl_rxbuf where 24038c2ecf20Sopenharmony_ci * packet and packet->buf_start are allocated 24048c2ecf20Sopenharmony_ci * separately using kmalloc(). For other endpoint 24058c2ecf20Sopenharmony_ci * rx_bufq, it is allocated as skb where packet is 24068c2ecf20Sopenharmony_ci * skb->head. Take care of this difference while freeing 24078c2ecf20Sopenharmony_ci * the memory. 24088c2ecf20Sopenharmony_ci */ 24098c2ecf20Sopenharmony_ci if (packet->endpoint == ENDPOINT_0) { 24108c2ecf20Sopenharmony_ci kfree(packet->buf_start); 24118c2ecf20Sopenharmony_ci kfree(packet); 24128c2ecf20Sopenharmony_ci } else { 24138c2ecf20Sopenharmony_ci dev_kfree_skb(packet->pkt_cntxt); 24148c2ecf20Sopenharmony_ci } 24158c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 24168c2ecf20Sopenharmony_ci } 24178c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 24188c2ecf20Sopenharmony_ci } 24198c2ecf20Sopenharmony_ci} 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_conn_service(struct htc_target *target, 24228c2ecf20Sopenharmony_ci struct htc_service_connect_req *conn_req, 24238c2ecf20Sopenharmony_ci struct htc_service_connect_resp *conn_resp) 24248c2ecf20Sopenharmony_ci{ 24258c2ecf20Sopenharmony_ci struct htc_packet *rx_pkt = NULL; 24268c2ecf20Sopenharmony_ci struct htc_packet *tx_pkt = NULL; 24278c2ecf20Sopenharmony_ci struct htc_conn_service_resp *resp_msg; 24288c2ecf20Sopenharmony_ci struct htc_conn_service_msg *conn_msg; 24298c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 24308c2ecf20Sopenharmony_ci enum htc_endpoint_id assigned_ep = ENDPOINT_MAX; 24318c2ecf20Sopenharmony_ci unsigned int max_msg_sz = 0; 24328c2ecf20Sopenharmony_ci int status = 0; 24338c2ecf20Sopenharmony_ci u16 msg_id; 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_HTC, 24368c2ecf20Sopenharmony_ci "htc connect service target 0x%p service id 0x%x\n", 24378c2ecf20Sopenharmony_ci target, conn_req->svc_id); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci if (conn_req->svc_id == HTC_CTRL_RSVD_SVC) { 24408c2ecf20Sopenharmony_ci /* special case for pseudo control service */ 24418c2ecf20Sopenharmony_ci assigned_ep = ENDPOINT_0; 24428c2ecf20Sopenharmony_ci max_msg_sz = HTC_MAX_CTRL_MSG_LEN; 24438c2ecf20Sopenharmony_ci } else { 24448c2ecf20Sopenharmony_ci /* allocate a packet to send to the target */ 24458c2ecf20Sopenharmony_ci tx_pkt = htc_get_control_buf(target, true); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci if (!tx_pkt) 24488c2ecf20Sopenharmony_ci return -ENOMEM; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci conn_msg = (struct htc_conn_service_msg *)tx_pkt->buf; 24518c2ecf20Sopenharmony_ci memset(conn_msg, 0, sizeof(*conn_msg)); 24528c2ecf20Sopenharmony_ci conn_msg->msg_id = cpu_to_le16(HTC_MSG_CONN_SVC_ID); 24538c2ecf20Sopenharmony_ci conn_msg->svc_id = cpu_to_le16(conn_req->svc_id); 24548c2ecf20Sopenharmony_ci conn_msg->conn_flags = cpu_to_le16(conn_req->conn_flags); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci set_htc_pkt_info(tx_pkt, NULL, (u8 *) conn_msg, 24578c2ecf20Sopenharmony_ci sizeof(*conn_msg) + conn_msg->svc_meta_len, 24588c2ecf20Sopenharmony_ci ENDPOINT_0, HTC_SERVICE_TX_PACKET_TAG); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_ci /* we want synchronous operation */ 24618c2ecf20Sopenharmony_ci tx_pkt->completion = NULL; 24628c2ecf20Sopenharmony_ci ath6kl_htc_tx_prep_pkt(tx_pkt, 0, 0, 0); 24638c2ecf20Sopenharmony_ci status = ath6kl_htc_tx_issue(target, tx_pkt); 24648c2ecf20Sopenharmony_ci 24658c2ecf20Sopenharmony_ci if (status) 24668c2ecf20Sopenharmony_ci goto fail_tx; 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_ci /* wait for response */ 24698c2ecf20Sopenharmony_ci rx_pkt = htc_wait_for_ctrl_msg(target); 24708c2ecf20Sopenharmony_ci 24718c2ecf20Sopenharmony_ci if (!rx_pkt) { 24728c2ecf20Sopenharmony_ci status = -ENOMEM; 24738c2ecf20Sopenharmony_ci goto fail_tx; 24748c2ecf20Sopenharmony_ci } 24758c2ecf20Sopenharmony_ci 24768c2ecf20Sopenharmony_ci resp_msg = (struct htc_conn_service_resp *)rx_pkt->buf; 24778c2ecf20Sopenharmony_ci msg_id = le16_to_cpu(resp_msg->msg_id); 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci if ((msg_id != HTC_MSG_CONN_SVC_RESP_ID) || 24808c2ecf20Sopenharmony_ci (rx_pkt->act_len < sizeof(*resp_msg))) { 24818c2ecf20Sopenharmony_ci status = -ENOMEM; 24828c2ecf20Sopenharmony_ci goto fail_tx; 24838c2ecf20Sopenharmony_ci } 24848c2ecf20Sopenharmony_ci 24858c2ecf20Sopenharmony_ci conn_resp->resp_code = resp_msg->status; 24868c2ecf20Sopenharmony_ci /* check response status */ 24878c2ecf20Sopenharmony_ci if (resp_msg->status != HTC_SERVICE_SUCCESS) { 24888c2ecf20Sopenharmony_ci ath6kl_err("target failed service 0x%X connect request (status:%d)\n", 24898c2ecf20Sopenharmony_ci resp_msg->svc_id, resp_msg->status); 24908c2ecf20Sopenharmony_ci status = -ENOMEM; 24918c2ecf20Sopenharmony_ci goto fail_tx; 24928c2ecf20Sopenharmony_ci } 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci assigned_ep = (enum htc_endpoint_id)resp_msg->eid; 24958c2ecf20Sopenharmony_ci max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); 24968c2ecf20Sopenharmony_ci } 24978c2ecf20Sopenharmony_ci 24988c2ecf20Sopenharmony_ci if (WARN_ON_ONCE(assigned_ep == ENDPOINT_UNUSED || 24998c2ecf20Sopenharmony_ci assigned_ep >= ENDPOINT_MAX || !max_msg_sz)) { 25008c2ecf20Sopenharmony_ci status = -ENOMEM; 25018c2ecf20Sopenharmony_ci goto fail_tx; 25028c2ecf20Sopenharmony_ci } 25038c2ecf20Sopenharmony_ci 25048c2ecf20Sopenharmony_ci endpoint = &target->endpoint[assigned_ep]; 25058c2ecf20Sopenharmony_ci endpoint->eid = assigned_ep; 25068c2ecf20Sopenharmony_ci if (endpoint->svc_id) { 25078c2ecf20Sopenharmony_ci status = -ENOMEM; 25088c2ecf20Sopenharmony_ci goto fail_tx; 25098c2ecf20Sopenharmony_ci } 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci /* return assigned endpoint to caller */ 25128c2ecf20Sopenharmony_ci conn_resp->endpoint = assigned_ep; 25138c2ecf20Sopenharmony_ci conn_resp->len_max = max_msg_sz; 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci /* setup the endpoint */ 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci /* this marks the endpoint in use */ 25188c2ecf20Sopenharmony_ci endpoint->svc_id = conn_req->svc_id; 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci endpoint->max_txq_depth = conn_req->max_txq_depth; 25218c2ecf20Sopenharmony_ci endpoint->len_max = max_msg_sz; 25228c2ecf20Sopenharmony_ci endpoint->ep_cb = conn_req->ep_cb; 25238c2ecf20Sopenharmony_ci endpoint->cred_dist.svc_id = conn_req->svc_id; 25248c2ecf20Sopenharmony_ci endpoint->cred_dist.htc_ep = endpoint; 25258c2ecf20Sopenharmony_ci endpoint->cred_dist.endpoint = assigned_ep; 25268c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_sz = target->tgt_cred_sz; 25278c2ecf20Sopenharmony_ci 25288c2ecf20Sopenharmony_ci switch (endpoint->svc_id) { 25298c2ecf20Sopenharmony_ci case WMI_DATA_BK_SVC: 25308c2ecf20Sopenharmony_ci endpoint->tx_drop_packet_threshold = MAX_DEF_COOKIE_NUM / 3; 25318c2ecf20Sopenharmony_ci break; 25328c2ecf20Sopenharmony_ci default: 25338c2ecf20Sopenharmony_ci endpoint->tx_drop_packet_threshold = MAX_HI_COOKIE_NUM; 25348c2ecf20Sopenharmony_ci break; 25358c2ecf20Sopenharmony_ci } 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci if (conn_req->max_rxmsg_sz) { 25388c2ecf20Sopenharmony_ci /* 25398c2ecf20Sopenharmony_ci * Override cred_per_msg calculation, this optimizes 25408c2ecf20Sopenharmony_ci * the credit-low indications since the host will actually 25418c2ecf20Sopenharmony_ci * issue smaller messages in the Send path. 25428c2ecf20Sopenharmony_ci */ 25438c2ecf20Sopenharmony_ci if (conn_req->max_rxmsg_sz > max_msg_sz) { 25448c2ecf20Sopenharmony_ci status = -ENOMEM; 25458c2ecf20Sopenharmony_ci goto fail_tx; 25468c2ecf20Sopenharmony_ci } 25478c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_per_msg = 25488c2ecf20Sopenharmony_ci conn_req->max_rxmsg_sz / target->tgt_cred_sz; 25498c2ecf20Sopenharmony_ci } else 25508c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_per_msg = 25518c2ecf20Sopenharmony_ci max_msg_sz / target->tgt_cred_sz; 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci if (!endpoint->cred_dist.cred_per_msg) 25548c2ecf20Sopenharmony_ci endpoint->cred_dist.cred_per_msg = 1; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci /* save local connection flags */ 25578c2ecf20Sopenharmony_ci endpoint->conn_flags = conn_req->flags; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_cifail_tx: 25608c2ecf20Sopenharmony_ci if (tx_pkt) 25618c2ecf20Sopenharmony_ci htc_reclaim_txctrl_buf(target, tx_pkt); 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if (rx_pkt) { 25648c2ecf20Sopenharmony_ci htc_rxpkt_reset(rx_pkt); 25658c2ecf20Sopenharmony_ci reclaim_rx_ctrl_buf(target, rx_pkt); 25668c2ecf20Sopenharmony_ci } 25678c2ecf20Sopenharmony_ci 25688c2ecf20Sopenharmony_ci return status; 25698c2ecf20Sopenharmony_ci} 25708c2ecf20Sopenharmony_ci 25718c2ecf20Sopenharmony_cistatic void reset_ep_state(struct htc_target *target) 25728c2ecf20Sopenharmony_ci{ 25738c2ecf20Sopenharmony_ci struct htc_endpoint *endpoint; 25748c2ecf20Sopenharmony_ci int i; 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_ci for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { 25778c2ecf20Sopenharmony_ci endpoint = &target->endpoint[i]; 25788c2ecf20Sopenharmony_ci memset(&endpoint->cred_dist, 0, sizeof(endpoint->cred_dist)); 25798c2ecf20Sopenharmony_ci endpoint->svc_id = 0; 25808c2ecf20Sopenharmony_ci endpoint->len_max = 0; 25818c2ecf20Sopenharmony_ci endpoint->max_txq_depth = 0; 25828c2ecf20Sopenharmony_ci memset(&endpoint->ep_st, 0, 25838c2ecf20Sopenharmony_ci sizeof(endpoint->ep_st)); 25848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&endpoint->rx_bufq); 25858c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&endpoint->txq); 25868c2ecf20Sopenharmony_ci endpoint->target = target; 25878c2ecf20Sopenharmony_ci } 25888c2ecf20Sopenharmony_ci 25898c2ecf20Sopenharmony_ci /* reset distribution list */ 25908c2ecf20Sopenharmony_ci /* FIXME: free existing entries */ 25918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&target->cred_dist_list); 25928c2ecf20Sopenharmony_ci} 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_get_rxbuf_num(struct htc_target *target, 25958c2ecf20Sopenharmony_ci enum htc_endpoint_id endpoint) 25968c2ecf20Sopenharmony_ci{ 25978c2ecf20Sopenharmony_ci int num; 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci spin_lock_bh(&target->rx_lock); 26008c2ecf20Sopenharmony_ci num = get_queue_depth(&(target->endpoint[endpoint].rx_bufq)); 26018c2ecf20Sopenharmony_ci spin_unlock_bh(&target->rx_lock); 26028c2ecf20Sopenharmony_ci return num; 26038c2ecf20Sopenharmony_ci} 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_cistatic void htc_setup_msg_bndl(struct htc_target *target) 26068c2ecf20Sopenharmony_ci{ 26078c2ecf20Sopenharmony_ci /* limit what HTC can handle */ 26088c2ecf20Sopenharmony_ci target->msg_per_bndl_max = min(HTC_HOST_MAX_MSG_PER_BUNDLE, 26098c2ecf20Sopenharmony_ci target->msg_per_bndl_max); 26108c2ecf20Sopenharmony_ci 26118c2ecf20Sopenharmony_ci if (ath6kl_hif_enable_scatter(target->dev->ar)) { 26128c2ecf20Sopenharmony_ci target->msg_per_bndl_max = 0; 26138c2ecf20Sopenharmony_ci return; 26148c2ecf20Sopenharmony_ci } 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci /* limit bundle what the device layer can handle */ 26178c2ecf20Sopenharmony_ci target->msg_per_bndl_max = min(target->max_scat_entries, 26188c2ecf20Sopenharmony_ci target->msg_per_bndl_max); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 26218c2ecf20Sopenharmony_ci "htc bundling allowed msg_per_bndl_max %d\n", 26228c2ecf20Sopenharmony_ci target->msg_per_bndl_max); 26238c2ecf20Sopenharmony_ci 26248c2ecf20Sopenharmony_ci /* Max rx bundle size is limited by the max tx bundle size */ 26258c2ecf20Sopenharmony_ci target->max_rx_bndl_sz = target->max_xfer_szper_scatreq; 26268c2ecf20Sopenharmony_ci /* Max tx bundle size if limited by the extended mbox address range */ 26278c2ecf20Sopenharmony_ci target->max_tx_bndl_sz = min(HIF_MBOX0_EXT_WIDTH, 26288c2ecf20Sopenharmony_ci target->max_xfer_szper_scatreq); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "htc max_rx_bndl_sz %d max_tx_bndl_sz %d\n", 26318c2ecf20Sopenharmony_ci target->max_rx_bndl_sz, target->max_tx_bndl_sz); 26328c2ecf20Sopenharmony_ci 26338c2ecf20Sopenharmony_ci if (target->max_tx_bndl_sz) 26348c2ecf20Sopenharmony_ci /* tx_bndl_mask is enabled per AC, each has 1 bit */ 26358c2ecf20Sopenharmony_ci target->tx_bndl_mask = (1 << WMM_NUM_AC) - 1; 26368c2ecf20Sopenharmony_ci 26378c2ecf20Sopenharmony_ci if (target->max_rx_bndl_sz) 26388c2ecf20Sopenharmony_ci target->rx_bndl_enable = true; 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci if ((target->tgt_cred_sz % target->block_sz) != 0) { 26418c2ecf20Sopenharmony_ci ath6kl_warn("credit size: %d is not block aligned! Disabling send bundling\n", 26428c2ecf20Sopenharmony_ci target->tgt_cred_sz); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci /* 26458c2ecf20Sopenharmony_ci * Disallow send bundling since the credit size is 26468c2ecf20Sopenharmony_ci * not aligned to a block size the I/O block 26478c2ecf20Sopenharmony_ci * padding will spill into the next credit buffer 26488c2ecf20Sopenharmony_ci * which is fatal. 26498c2ecf20Sopenharmony_ci */ 26508c2ecf20Sopenharmony_ci target->tx_bndl_mask = 0; 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci} 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_wait_target(struct htc_target *target) 26558c2ecf20Sopenharmony_ci{ 26568c2ecf20Sopenharmony_ci struct htc_packet *packet = NULL; 26578c2ecf20Sopenharmony_ci struct htc_ready_ext_msg *rdy_msg; 26588c2ecf20Sopenharmony_ci struct htc_service_connect_req connect; 26598c2ecf20Sopenharmony_ci struct htc_service_connect_resp resp; 26608c2ecf20Sopenharmony_ci int status; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci /* we should be getting 1 control message that the target is ready */ 26638c2ecf20Sopenharmony_ci packet = htc_wait_for_ctrl_msg(target); 26648c2ecf20Sopenharmony_ci 26658c2ecf20Sopenharmony_ci if (!packet) 26668c2ecf20Sopenharmony_ci return -ENOMEM; 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci /* we controlled the buffer creation so it's properly aligned */ 26698c2ecf20Sopenharmony_ci rdy_msg = (struct htc_ready_ext_msg *)packet->buf; 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci if ((le16_to_cpu(rdy_msg->ver2_0_info.msg_id) != HTC_MSG_READY_ID) || 26728c2ecf20Sopenharmony_ci (packet->act_len < sizeof(struct htc_ready_msg))) { 26738c2ecf20Sopenharmony_ci status = -ENOMEM; 26748c2ecf20Sopenharmony_ci goto fail_wait_target; 26758c2ecf20Sopenharmony_ci } 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci if (!rdy_msg->ver2_0_info.cred_cnt || !rdy_msg->ver2_0_info.cred_sz) { 26788c2ecf20Sopenharmony_ci status = -ENOMEM; 26798c2ecf20Sopenharmony_ci goto fail_wait_target; 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci 26828c2ecf20Sopenharmony_ci target->tgt_creds = le16_to_cpu(rdy_msg->ver2_0_info.cred_cnt); 26838c2ecf20Sopenharmony_ci target->tgt_cred_sz = le16_to_cpu(rdy_msg->ver2_0_info.cred_sz); 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, 26868c2ecf20Sopenharmony_ci "htc target ready credits %d size %d\n", 26878c2ecf20Sopenharmony_ci target->tgt_creds, target->tgt_cred_sz); 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci /* check if this is an extended ready message */ 26908c2ecf20Sopenharmony_ci if (packet->act_len >= sizeof(struct htc_ready_ext_msg)) { 26918c2ecf20Sopenharmony_ci /* this is an extended message */ 26928c2ecf20Sopenharmony_ci target->htc_tgt_ver = rdy_msg->htc_ver; 26938c2ecf20Sopenharmony_ci target->msg_per_bndl_max = rdy_msg->msg_per_htc_bndl; 26948c2ecf20Sopenharmony_ci } else { 26958c2ecf20Sopenharmony_ci /* legacy */ 26968c2ecf20Sopenharmony_ci target->htc_tgt_ver = HTC_VERSION_2P0; 26978c2ecf20Sopenharmony_ci target->msg_per_bndl_max = 0; 26988c2ecf20Sopenharmony_ci } 26998c2ecf20Sopenharmony_ci 27008c2ecf20Sopenharmony_ci ath6kl_dbg(ATH6KL_DBG_BOOT, "htc using protocol %s (%d)\n", 27018c2ecf20Sopenharmony_ci (target->htc_tgt_ver == HTC_VERSION_2P0) ? "2.0" : ">= 2.1", 27028c2ecf20Sopenharmony_ci target->htc_tgt_ver); 27038c2ecf20Sopenharmony_ci 27048c2ecf20Sopenharmony_ci if (target->msg_per_bndl_max > 0) 27058c2ecf20Sopenharmony_ci htc_setup_msg_bndl(target); 27068c2ecf20Sopenharmony_ci 27078c2ecf20Sopenharmony_ci /* setup our pseudo HTC control endpoint connection */ 27088c2ecf20Sopenharmony_ci memset(&connect, 0, sizeof(connect)); 27098c2ecf20Sopenharmony_ci memset(&resp, 0, sizeof(resp)); 27108c2ecf20Sopenharmony_ci connect.ep_cb.rx = htc_ctrl_rx; 27118c2ecf20Sopenharmony_ci connect.ep_cb.rx_refill = NULL; 27128c2ecf20Sopenharmony_ci connect.ep_cb.tx_full = NULL; 27138c2ecf20Sopenharmony_ci connect.max_txq_depth = NUM_CONTROL_BUFFERS; 27148c2ecf20Sopenharmony_ci connect.svc_id = HTC_CTRL_RSVD_SVC; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci /* connect fake service */ 27178c2ecf20Sopenharmony_ci status = ath6kl_htc_mbox_conn_service((void *)target, &connect, &resp); 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_ci if (status) 27208c2ecf20Sopenharmony_ci /* 27218c2ecf20Sopenharmony_ci * FIXME: this call doesn't make sense, the caller should 27228c2ecf20Sopenharmony_ci * call ath6kl_htc_mbox_cleanup() when it wants remove htc 27238c2ecf20Sopenharmony_ci */ 27248c2ecf20Sopenharmony_ci ath6kl_hif_cleanup_scatter(target->dev->ar); 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_cifail_wait_target: 27278c2ecf20Sopenharmony_ci if (packet) { 27288c2ecf20Sopenharmony_ci htc_rxpkt_reset(packet); 27298c2ecf20Sopenharmony_ci reclaim_rx_ctrl_buf(target, packet); 27308c2ecf20Sopenharmony_ci } 27318c2ecf20Sopenharmony_ci 27328c2ecf20Sopenharmony_ci return status; 27338c2ecf20Sopenharmony_ci} 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci/* 27368c2ecf20Sopenharmony_ci * Start HTC, enable interrupts and let the target know 27378c2ecf20Sopenharmony_ci * host has finished setup. 27388c2ecf20Sopenharmony_ci */ 27398c2ecf20Sopenharmony_cistatic int ath6kl_htc_mbox_start(struct htc_target *target) 27408c2ecf20Sopenharmony_ci{ 27418c2ecf20Sopenharmony_ci struct htc_packet *packet; 27428c2ecf20Sopenharmony_ci int status; 27438c2ecf20Sopenharmony_ci 27448c2ecf20Sopenharmony_ci memset(&target->dev->irq_proc_reg, 0, 27458c2ecf20Sopenharmony_ci sizeof(target->dev->irq_proc_reg)); 27468c2ecf20Sopenharmony_ci 27478c2ecf20Sopenharmony_ci /* Disable interrupts at the chip level */ 27488c2ecf20Sopenharmony_ci ath6kl_hif_disable_intrs(target->dev); 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci target->htc_flags = 0; 27518c2ecf20Sopenharmony_ci target->rx_st_flags = 0; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci /* Push control receive buffers into htc control endpoint */ 27548c2ecf20Sopenharmony_ci while ((packet = htc_get_control_buf(target, false)) != NULL) { 27558c2ecf20Sopenharmony_ci status = htc_add_rxbuf(target, packet); 27568c2ecf20Sopenharmony_ci if (status) 27578c2ecf20Sopenharmony_ci return status; 27588c2ecf20Sopenharmony_ci } 27598c2ecf20Sopenharmony_ci 27608c2ecf20Sopenharmony_ci /* NOTE: the first entry in the distribution list is ENDPOINT_0 */ 27618c2ecf20Sopenharmony_ci ath6kl_credit_init(target->credit_info, &target->cred_dist_list, 27628c2ecf20Sopenharmony_ci target->tgt_creds); 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci dump_cred_dist_stats(target); 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci /* Indicate to the target of the setup completion */ 27678c2ecf20Sopenharmony_ci status = htc_setup_tx_complete(target); 27688c2ecf20Sopenharmony_ci 27698c2ecf20Sopenharmony_ci if (status) 27708c2ecf20Sopenharmony_ci return status; 27718c2ecf20Sopenharmony_ci 27728c2ecf20Sopenharmony_ci /* unmask interrupts */ 27738c2ecf20Sopenharmony_ci status = ath6kl_hif_unmask_intrs(target->dev); 27748c2ecf20Sopenharmony_ci 27758c2ecf20Sopenharmony_ci if (status) 27768c2ecf20Sopenharmony_ci ath6kl_htc_mbox_stop(target); 27778c2ecf20Sopenharmony_ci 27788c2ecf20Sopenharmony_ci return status; 27798c2ecf20Sopenharmony_ci} 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_cistatic int ath6kl_htc_reset(struct htc_target *target) 27828c2ecf20Sopenharmony_ci{ 27838c2ecf20Sopenharmony_ci u32 block_size, ctrl_bufsz; 27848c2ecf20Sopenharmony_ci struct htc_packet *packet; 27858c2ecf20Sopenharmony_ci int i; 27868c2ecf20Sopenharmony_ci 27878c2ecf20Sopenharmony_ci reset_ep_state(target); 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci block_size = target->dev->ar->mbox_info.block_size; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci ctrl_bufsz = (block_size > HTC_MAX_CTRL_MSG_LEN) ? 27928c2ecf20Sopenharmony_ci (block_size + HTC_HDR_LENGTH) : 27938c2ecf20Sopenharmony_ci (HTC_MAX_CTRL_MSG_LEN + HTC_HDR_LENGTH); 27948c2ecf20Sopenharmony_ci 27958c2ecf20Sopenharmony_ci for (i = 0; i < NUM_CONTROL_BUFFERS; i++) { 27968c2ecf20Sopenharmony_ci packet = kzalloc(sizeof(*packet), GFP_KERNEL); 27978c2ecf20Sopenharmony_ci if (!packet) 27988c2ecf20Sopenharmony_ci return -ENOMEM; 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci packet->buf_start = kzalloc(ctrl_bufsz, GFP_KERNEL); 28018c2ecf20Sopenharmony_ci if (!packet->buf_start) { 28028c2ecf20Sopenharmony_ci kfree(packet); 28038c2ecf20Sopenharmony_ci return -ENOMEM; 28048c2ecf20Sopenharmony_ci } 28058c2ecf20Sopenharmony_ci 28068c2ecf20Sopenharmony_ci packet->buf_len = ctrl_bufsz; 28078c2ecf20Sopenharmony_ci if (i < NUM_CONTROL_RX_BUFFERS) { 28088c2ecf20Sopenharmony_ci packet->act_len = 0; 28098c2ecf20Sopenharmony_ci packet->buf = packet->buf_start; 28108c2ecf20Sopenharmony_ci packet->endpoint = ENDPOINT_0; 28118c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &target->free_ctrl_rxbuf); 28128c2ecf20Sopenharmony_ci } else { 28138c2ecf20Sopenharmony_ci list_add_tail(&packet->list, &target->free_ctrl_txbuf); 28148c2ecf20Sopenharmony_ci } 28158c2ecf20Sopenharmony_ci } 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci return 0; 28188c2ecf20Sopenharmony_ci} 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci/* htc_stop: stop interrupt reception, and flush all queued buffers */ 28218c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_stop(struct htc_target *target) 28228c2ecf20Sopenharmony_ci{ 28238c2ecf20Sopenharmony_ci spin_lock_bh(&target->htc_lock); 28248c2ecf20Sopenharmony_ci target->htc_flags |= HTC_OP_STATE_STOPPING; 28258c2ecf20Sopenharmony_ci spin_unlock_bh(&target->htc_lock); 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_ci /* 28288c2ecf20Sopenharmony_ci * Masking interrupts is a synchronous operation, when this 28298c2ecf20Sopenharmony_ci * function returns all pending HIF I/O has completed, we can 28308c2ecf20Sopenharmony_ci * safely flush the queues. 28318c2ecf20Sopenharmony_ci */ 28328c2ecf20Sopenharmony_ci ath6kl_hif_mask_intrs(target->dev); 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci ath6kl_htc_flush_txep_all(target); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci ath6kl_htc_mbox_flush_rx_buf(target); 28378c2ecf20Sopenharmony_ci 28388c2ecf20Sopenharmony_ci ath6kl_htc_reset(target); 28398c2ecf20Sopenharmony_ci} 28408c2ecf20Sopenharmony_ci 28418c2ecf20Sopenharmony_cistatic void *ath6kl_htc_mbox_create(struct ath6kl *ar) 28428c2ecf20Sopenharmony_ci{ 28438c2ecf20Sopenharmony_ci struct htc_target *target = NULL; 28448c2ecf20Sopenharmony_ci int status = 0; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci target = kzalloc(sizeof(*target), GFP_KERNEL); 28478c2ecf20Sopenharmony_ci if (!target) { 28488c2ecf20Sopenharmony_ci ath6kl_err("unable to allocate memory\n"); 28498c2ecf20Sopenharmony_ci return NULL; 28508c2ecf20Sopenharmony_ci } 28518c2ecf20Sopenharmony_ci 28528c2ecf20Sopenharmony_ci target->dev = kzalloc(sizeof(*target->dev), GFP_KERNEL); 28538c2ecf20Sopenharmony_ci if (!target->dev) { 28548c2ecf20Sopenharmony_ci ath6kl_err("unable to allocate memory\n"); 28558c2ecf20Sopenharmony_ci kfree(target); 28568c2ecf20Sopenharmony_ci return NULL; 28578c2ecf20Sopenharmony_ci } 28588c2ecf20Sopenharmony_ci 28598c2ecf20Sopenharmony_ci spin_lock_init(&target->htc_lock); 28608c2ecf20Sopenharmony_ci spin_lock_init(&target->rx_lock); 28618c2ecf20Sopenharmony_ci spin_lock_init(&target->tx_lock); 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&target->free_ctrl_txbuf); 28648c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&target->free_ctrl_rxbuf); 28658c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&target->cred_dist_list); 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_ci target->dev->ar = ar; 28688c2ecf20Sopenharmony_ci target->dev->htc_cnxt = target; 28698c2ecf20Sopenharmony_ci target->ep_waiting = ENDPOINT_MAX; 28708c2ecf20Sopenharmony_ci 28718c2ecf20Sopenharmony_ci status = ath6kl_hif_setup(target->dev); 28728c2ecf20Sopenharmony_ci if (status) 28738c2ecf20Sopenharmony_ci goto err_htc_cleanup; 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci status = ath6kl_htc_reset(target); 28768c2ecf20Sopenharmony_ci if (status) 28778c2ecf20Sopenharmony_ci goto err_htc_cleanup; 28788c2ecf20Sopenharmony_ci 28798c2ecf20Sopenharmony_ci return target; 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_cierr_htc_cleanup: 28828c2ecf20Sopenharmony_ci ath6kl_htc_mbox_cleanup(target); 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_ci return NULL; 28858c2ecf20Sopenharmony_ci} 28868c2ecf20Sopenharmony_ci 28878c2ecf20Sopenharmony_ci/* cleanup the HTC instance */ 28888c2ecf20Sopenharmony_cistatic void ath6kl_htc_mbox_cleanup(struct htc_target *target) 28898c2ecf20Sopenharmony_ci{ 28908c2ecf20Sopenharmony_ci struct htc_packet *packet, *tmp_packet; 28918c2ecf20Sopenharmony_ci 28928c2ecf20Sopenharmony_ci ath6kl_hif_cleanup_scatter(target->dev->ar); 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_packet, 28958c2ecf20Sopenharmony_ci &target->free_ctrl_txbuf, list) { 28968c2ecf20Sopenharmony_ci list_del(&packet->list); 28978c2ecf20Sopenharmony_ci kfree(packet->buf_start); 28988c2ecf20Sopenharmony_ci kfree(packet); 28998c2ecf20Sopenharmony_ci } 29008c2ecf20Sopenharmony_ci 29018c2ecf20Sopenharmony_ci list_for_each_entry_safe(packet, tmp_packet, 29028c2ecf20Sopenharmony_ci &target->free_ctrl_rxbuf, list) { 29038c2ecf20Sopenharmony_ci list_del(&packet->list); 29048c2ecf20Sopenharmony_ci kfree(packet->buf_start); 29058c2ecf20Sopenharmony_ci kfree(packet); 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci 29088c2ecf20Sopenharmony_ci kfree(target->dev); 29098c2ecf20Sopenharmony_ci kfree(target); 29108c2ecf20Sopenharmony_ci} 29118c2ecf20Sopenharmony_ci 29128c2ecf20Sopenharmony_cistatic const struct ath6kl_htc_ops ath6kl_htc_mbox_ops = { 29138c2ecf20Sopenharmony_ci .create = ath6kl_htc_mbox_create, 29148c2ecf20Sopenharmony_ci .wait_target = ath6kl_htc_mbox_wait_target, 29158c2ecf20Sopenharmony_ci .start = ath6kl_htc_mbox_start, 29168c2ecf20Sopenharmony_ci .conn_service = ath6kl_htc_mbox_conn_service, 29178c2ecf20Sopenharmony_ci .tx = ath6kl_htc_mbox_tx, 29188c2ecf20Sopenharmony_ci .stop = ath6kl_htc_mbox_stop, 29198c2ecf20Sopenharmony_ci .cleanup = ath6kl_htc_mbox_cleanup, 29208c2ecf20Sopenharmony_ci .flush_txep = ath6kl_htc_mbox_flush_txep, 29218c2ecf20Sopenharmony_ci .flush_rx_buf = ath6kl_htc_mbox_flush_rx_buf, 29228c2ecf20Sopenharmony_ci .activity_changed = ath6kl_htc_mbox_activity_changed, 29238c2ecf20Sopenharmony_ci .get_rxbuf_num = ath6kl_htc_mbox_get_rxbuf_num, 29248c2ecf20Sopenharmony_ci .add_rxbuf_multiple = ath6kl_htc_mbox_add_rxbuf_multiple, 29258c2ecf20Sopenharmony_ci .credit_setup = ath6kl_htc_mbox_credit_setup, 29268c2ecf20Sopenharmony_ci}; 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_civoid ath6kl_htc_mbox_attach(struct ath6kl *ar) 29298c2ecf20Sopenharmony_ci{ 29308c2ecf20Sopenharmony_ci ar->htc_ops = &ath6kl_htc_mbox_ops; 29318c2ecf20Sopenharmony_ci} 2932