4 * Copyright (c) 2004-2007 Atheros Communications Inc.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation;
12 * Software distributed under the License is distributed on an "AS
13 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * rights and limitations under the License.
27 #include "common_drv.h"
29 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
31 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
34 #define DATA_SVCS_USED 3
36 #define DATA_SVCS_USED 4
39 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO
*pCredInfo
,
40 HTC_ENDPOINT_CREDIT_DIST
*pEPDistList
);
42 static void SeekCredits(COMMON_CREDIT_STATE_INFO
*pCredInfo
,
43 HTC_ENDPOINT_CREDIT_DIST
*pEPDistList
);
45 /* reduce an ep's credits back to a set limit */
46 static INLINE
void ReduceCredits(COMMON_CREDIT_STATE_INFO
*pCredInfo
,
47 HTC_ENDPOINT_CREDIT_DIST
*pEpDist
,
52 /* set the new limit */
53 pEpDist
->TxCreditsAssigned
= Limit
;
55 if (pEpDist
->TxCredits
<= Limit
) {
59 /* figure out how much to take away */
60 credits
= pEpDist
->TxCredits
- Limit
;
62 pEpDist
->TxCredits
-= credits
;
63 pCredInfo
->CurrentFreeCredits
+= credits
;
66 /* give an endpoint some credits from the free credit pool */
67 #define GiveCredits(pCredInfo,pEpDist,credits) \
69 (pEpDist)->TxCredits += (credits); \
70 (pEpDist)->TxCreditsAssigned += (credits); \
71 (pCredInfo)->CurrentFreeCredits -= (credits); \
75 /* default credit init callback.
76 * This function is called in the context of HTCStart() to setup initial (application-specific)
77 * credit distributions */
78 static void ar6000_credit_init(void *Context
,
79 HTC_ENDPOINT_CREDIT_DIST
*pEPList
,
82 HTC_ENDPOINT_CREDIT_DIST
*pCurEpDist
;
84 COMMON_CREDIT_STATE_INFO
*pCredInfo
= (COMMON_CREDIT_STATE_INFO
*)Context
;
86 pCredInfo
->CurrentFreeCredits
= TotalCredits
;
87 pCredInfo
->TotalAvailableCredits
= TotalCredits
;
91 /* run through the list and initialize */
92 while (pCurEpDist
!= NULL
) {
94 /* set minimums for each endpoint */
95 pCurEpDist
->TxCreditsMin
= pCurEpDist
->TxCreditsPerMaxMsg
;
97 if (pCurEpDist
->ServiceID
== WMI_CONTROL_SVC
) {
98 /* give control service some credits */
99 GiveCredits(pCredInfo
,pCurEpDist
,pCurEpDist
->TxCreditsMin
);
100 /* control service is always marked active, it never goes inactive EVER */
101 SET_EP_ACTIVE(pCurEpDist
);
102 } else if (pCurEpDist
->ServiceID
== WMI_DATA_BK_SVC
) {
103 /* this is the lowest priority data endpoint, save this off for easy access */
104 pCredInfo
->pLowestPriEpDist
= pCurEpDist
;
107 /* Streams have to be created (explicit | implicit)for all kinds
108 * of traffic. BE endpoints are also inactive in the beginning.
109 * When BE traffic starts it creates implicit streams that
110 * redistributes credits.
113 /* note, all other endpoints have minimums set but are initially given NO credits.
114 * Credits will be distributed as traffic activity demands */
115 pCurEpDist
= pCurEpDist
->pNext
;
118 if (pCredInfo
->CurrentFreeCredits
<= 0) {
119 AR_DEBUG_PRINTF(ATH_LOG_INF
, ("Not enough credits (%d) to do credit distributions \n", TotalCredits
));
125 pCurEpDist
= pEPList
;
126 /* now run through the list and set max operating credit limits for everyone */
127 while (pCurEpDist
!= NULL
) {
128 if (pCurEpDist
->ServiceID
== WMI_CONTROL_SVC
) {
129 /* control service max is just 1 max message */
130 pCurEpDist
->TxCreditsNorm
= pCurEpDist
->TxCreditsPerMaxMsg
;
132 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
134 * We use a simple calculation here, we take the remaining credits and
135 * determine how many max messages this can cover and then set each endpoint's
136 * normal value equal to half this amount.
138 count
= (pCredInfo
->CurrentFreeCredits
/pCurEpDist
->TxCreditsPerMaxMsg
) * pCurEpDist
->TxCreditsPerMaxMsg
;
140 count
= max(count
,pCurEpDist
->TxCreditsPerMaxMsg
);
142 pCurEpDist
->TxCreditsNorm
= count
;
145 pCurEpDist
= pCurEpDist
->pNext
;
151 /* default credit distribution callback
152 * This callback is invoked whenever endpoints require credit distributions.
153 * A lock is held while this function is invoked, this function shall NOT block.
154 * The pEPDistList is a list of distribution structures in prioritized order as
155 * defined by the call to the HTCSetCreditDistribution() api.
158 static void ar6000_credit_distribute(void *Context
,
159 HTC_ENDPOINT_CREDIT_DIST
*pEPDistList
,
160 HTC_CREDIT_DIST_REASON Reason
)
162 HTC_ENDPOINT_CREDIT_DIST
*pCurEpDist
;
163 COMMON_CREDIT_STATE_INFO
*pCredInfo
= (COMMON_CREDIT_STATE_INFO
*)Context
;
166 case HTC_CREDIT_DIST_SEND_COMPLETE
:
167 pCurEpDist
= pEPDistList
;
168 /* we are given the start of the endpoint distribution list.
169 * There may be one or more endpoints to service.
170 * Run through the list and distribute credits */
171 while (pCurEpDist
!= NULL
) {
173 if (pCurEpDist
->TxCreditsToDist
> 0) {
174 /* return the credits back to the endpoint */
175 pCurEpDist
->TxCredits
+= pCurEpDist
->TxCreditsToDist
;
176 /* always zero out when we are done */
177 pCurEpDist
->TxCreditsToDist
= 0;
179 if (pCurEpDist
->TxCredits
> pCurEpDist
->TxCreditsAssigned
) {
180 /* reduce to the assigned limit, previous credit reductions
181 * could have caused the limit to change */
182 ReduceCredits(pCredInfo
, pCurEpDist
, pCurEpDist
->TxCreditsAssigned
);
185 if (pCurEpDist
->TxCredits
> pCurEpDist
->TxCreditsNorm
) {
186 /* oversubscribed endpoints need to reduce back to normal */
187 ReduceCredits(pCredInfo
, pCurEpDist
, pCurEpDist
->TxCreditsNorm
);
191 pCurEpDist
= pCurEpDist
->pNext
;
194 A_ASSERT(pCredInfo
->CurrentFreeCredits
<= pCredInfo
->TotalAvailableCredits
);
198 case HTC_CREDIT_DIST_ACTIVITY_CHANGE
:
199 RedistributeCredits(pCredInfo
,pEPDistList
);
201 case HTC_CREDIT_DIST_SEEK_CREDITS
:
202 SeekCredits(pCredInfo
,pEPDistList
);
204 case HTC_DUMP_CREDIT_STATE
:
205 AR_DEBUG_PRINTF(ATH_LOG_INF
, ("Credit Distribution, total : %d, free : %d\n",
206 pCredInfo
->TotalAvailableCredits
, pCredInfo
->CurrentFreeCredits
));
215 /* redistribute credits based on activity change */
216 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO
*pCredInfo
,
217 HTC_ENDPOINT_CREDIT_DIST
*pEPDistList
)
219 HTC_ENDPOINT_CREDIT_DIST
*pCurEpDist
= pEPDistList
;
221 /* walk through the list and remove credits from inactive endpoints */
222 while (pCurEpDist
!= NULL
) {
224 if (pCurEpDist
->ServiceID
!= WMI_CONTROL_SVC
) {
225 if (!IS_EP_ACTIVE(pCurEpDist
)) {
226 /* EP is inactive, reduce credits back to zero */
227 ReduceCredits(pCredInfo
, pCurEpDist
, 0);
231 /* NOTE in the active case, we do not need to do anything further,
232 * when an EP goes active and needs credits, HTC will call into
233 * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS */
235 pCurEpDist
= pCurEpDist
->pNext
;
238 A_ASSERT(pCredInfo
->CurrentFreeCredits
<= pCredInfo
->TotalAvailableCredits
);
242 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */
243 static void SeekCredits(COMMON_CREDIT_STATE_INFO
*pCredInfo
,
244 HTC_ENDPOINT_CREDIT_DIST
*pEPDist
)
246 HTC_ENDPOINT_CREDIT_DIST
*pCurEpDist
;
252 if (pEPDist
->ServiceID
== WMI_CONTROL_SVC
) {
253 /* we never oversubscribe on the control service, this is not
254 * a high performance path and the target never holds onto control
255 * credits for too long */
259 /* for all other services, we follow a simple algorithm of
260 * 1. checking the free pool for credits
261 * 2. checking lower priority endpoints for credits to take */
263 if (pCredInfo
->CurrentFreeCredits
>= 2 * pEPDist
->TxCreditsSeek
) {
264 /* try to give more credits than it needs */
265 credits
= 2 * pEPDist
->TxCreditsSeek
;
267 /* give what we can */
268 credits
= min(pCredInfo
->CurrentFreeCredits
,pEPDist
->TxCreditsSeek
);
271 if (credits
>= pEPDist
->TxCreditsSeek
) {
272 /* we found some to fullfill the seek request */
276 /* we don't have enough in the free pool, try taking away from lower priority services
278 * The rule for taking away credits:
279 * 1. Only take from lower priority endpoints
280 * 2. Only take what is allocated above the minimum (never starve an endpoint completely)
281 * 3. Only take what you need.
285 /* starting at the lowest priority */
286 pCurEpDist
= pCredInfo
->pLowestPriEpDist
;
288 /* work backwards until we hit the endpoint again */
289 while (pCurEpDist
!= pEPDist
) {
290 /* calculate how many we need so far */
291 need
= pEPDist
->TxCreditsSeek
- pCredInfo
->CurrentFreeCredits
;
293 if ((pCurEpDist
->TxCreditsAssigned
- need
) > pCurEpDist
->TxCreditsMin
) {
294 /* the current one has been allocated more than it's minimum and it
295 * has enough credits assigned above it's minimum to fullfill our need
296 * try to take away just enough to fullfill our need */
297 ReduceCredits(pCredInfo
,
299 pCurEpDist
->TxCreditsAssigned
- need
);
301 if (pCredInfo
->CurrentFreeCredits
>= pEPDist
->TxCreditsSeek
) {
307 pCurEpDist
= pCurEpDist
->pPrev
;
310 /* return what we can get */
311 credits
= min(pCredInfo
->CurrentFreeCredits
,pEPDist
->TxCreditsSeek
);
315 /* did we find some credits? */
317 /* give what we can */
318 GiveCredits(pCredInfo
, pEPDist
, credits
);
323 /* initialize and setup credit distribution */
324 A_STATUS
ar6000_setup_credit_dist(HTC_HANDLE HTCHandle
, COMMON_CREDIT_STATE_INFO
*pCredInfo
)
326 HTC_SERVICE_ID servicepriority
[5];
328 A_MEMZERO(pCredInfo
,sizeof(COMMON_CREDIT_STATE_INFO
));
330 servicepriority
[0] = WMI_CONTROL_SVC
; /* highest */
331 servicepriority
[1] = WMI_DATA_VO_SVC
;
332 servicepriority
[2] = WMI_DATA_VI_SVC
;
333 servicepriority
[3] = WMI_DATA_BE_SVC
;
334 servicepriority
[4] = WMI_DATA_BK_SVC
; /* lowest */
336 /* set callbacks and priority list */
337 HTCSetCreditDistribution(HTCHandle
,
339 ar6000_credit_distribute
,