Add ar6000 wireless driver.
[kernel.git] / drivers / ar6000 / miscdrv / credit_dist.c
1
2 /*
3  *
4  * Copyright (c) 2004-2007 Atheros Communications Inc.
5  * All rights reserved.
6  *
7  *
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;
11  *
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.
16  *
17  *
18  *
19  */
20
21 #include "a_config.h"
22 #include "athdefs.h"
23 #include "a_types.h"
24 #include "a_osapi.h"
25 #include "a_debug.h"
26 #include "htc_api.h"
27 #include "common_drv.h"
28
29 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
30
31 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
32
33 #ifdef NO_VO_SERVICE
34 #define DATA_SVCS_USED 3
35 #else
36 #define DATA_SVCS_USED 4
37 #endif
38
39 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
40                                 HTC_ENDPOINT_CREDIT_DIST *pEPDistList);
41
42 static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
43                         HTC_ENDPOINT_CREDIT_DIST *pEPDistList);
44
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,
48                                 int                       Limit)
49 {
50     int credits;
51
52         /* set the new limit */
53     pEpDist->TxCreditsAssigned = Limit;
54
55     if (pEpDist->TxCredits <= Limit) {
56         return;
57     }
58
59         /* figure out how much to take away */
60     credits = pEpDist->TxCredits - Limit;
61         /* take them away */
62     pEpDist->TxCredits -= credits;
63     pCredInfo->CurrentFreeCredits += credits;
64 }
65
66 /* give an endpoint some credits from the free credit pool */
67 #define GiveCredits(pCredInfo,pEpDist,credits)      \
68 {                                                   \
69     (pEpDist)->TxCredits += (credits);              \
70     (pEpDist)->TxCreditsAssigned += (credits);      \
71     (pCredInfo)->CurrentFreeCredits -= (credits);   \
72 }
73
74
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,
80                                int                      TotalCredits)
81 {
82     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
83     int                      count;
84     COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context;
85
86     pCredInfo->CurrentFreeCredits = TotalCredits;
87     pCredInfo->TotalAvailableCredits = TotalCredits;
88
89     pCurEpDist = pEPList;
90
91         /* run through the list and initialize */
92     while (pCurEpDist != NULL) {
93
94             /* set minimums for each endpoint */
95         pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
96
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;
105         }
106
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.
111          */
112
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;
116     }
117
118     if (pCredInfo->CurrentFreeCredits <= 0) {
119         AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits));
120         A_ASSERT(FALSE);
121         return;
122     }
123
124         /* reset list */
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;
131         } else {
132                 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
133                  * the same.
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.
137                  * */
138             count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg;
139             count = count >> 1;
140             count = max(count,pCurEpDist->TxCreditsPerMaxMsg);
141                 /* set normal */
142             pCurEpDist->TxCreditsNorm = count;
143
144         }
145         pCurEpDist = pCurEpDist->pNext;
146     }
147
148 }
149
150
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.
156  *
157  */
158 static void ar6000_credit_distribute(void                     *Context,
159                                      HTC_ENDPOINT_CREDIT_DIST *pEPDistList,
160                                      HTC_CREDIT_DIST_REASON   Reason)
161 {
162     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
163     COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context;
164
165     switch (Reason) {
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) {
172
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;
178
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);
183                     }
184
185                     if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) {
186                             /* oversubscribed endpoints need to reduce back to normal */
187                         ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm);
188                     }
189                 }
190
191                 pCurEpDist = pCurEpDist->pNext;
192             }
193
194             A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits);
195
196             break;
197
198         case HTC_CREDIT_DIST_ACTIVITY_CHANGE :
199             RedistributeCredits(pCredInfo,pEPDistList);
200             break;
201         case HTC_CREDIT_DIST_SEEK_CREDITS :
202             SeekCredits(pCredInfo,pEPDistList);
203             break;
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));
207             break;
208         default:
209             break;
210
211     }
212
213 }
214
215 /* redistribute credits based on activity change */
216 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
217                                 HTC_ENDPOINT_CREDIT_DIST *pEPDistList)
218 {
219     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist = pEPDistList;
220
221         /* walk through the list and remove credits from inactive endpoints */
222     while (pCurEpDist != NULL) {
223
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);
228             }
229         }
230
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  */
234
235         pCurEpDist = pCurEpDist->pNext;
236     }
237
238     A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits);
239
240 }
241
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)
245 {
246     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
247     int                      credits = 0;
248     int                      need;
249
250     do {
251
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 */
256             break;
257         }
258
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 */
262
263         if (pCredInfo->CurrentFreeCredits >= 2 * pEPDist->TxCreditsSeek) {
264                 /* try to give more credits than it needs */
265             credits = 2 * pEPDist->TxCreditsSeek;
266         } else {
267                 /* give what we can */
268             credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
269         }
270
271         if (credits >= pEPDist->TxCreditsSeek) {
272                 /* we found some to fullfill the seek request */
273             break;
274         }
275
276         /* we don't have enough in the free pool, try taking away from lower priority services
277          *
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.
282          *
283          * */
284
285             /* starting at the lowest priority */
286         pCurEpDist = pCredInfo->pLowestPriEpDist;
287
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;
292
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,
298                               pCurEpDist,
299                               pCurEpDist->TxCreditsAssigned - need);
300
301                 if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) {
302                         /* we have enough */
303                     break;
304                 }
305             }
306
307             pCurEpDist = pCurEpDist->pPrev;
308         }
309
310             /* return what we can get */
311         credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
312
313     } while (FALSE);
314
315         /* did we find some credits? */
316     if (credits) {
317             /* give what we can */
318         GiveCredits(pCredInfo, pEPDist, credits);
319     }
320
321 }
322
323 /* initialize and setup credit distribution */
324 A_STATUS ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, COMMON_CREDIT_STATE_INFO *pCredInfo)
325 {
326     HTC_SERVICE_ID servicepriority[5];
327
328     A_MEMZERO(pCredInfo,sizeof(COMMON_CREDIT_STATE_INFO));
329
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 */
335
336         /* set callbacks and priority list */
337     HTCSetCreditDistribution(HTCHandle,
338                              pCredInfo,
339                              ar6000_credit_distribute,
340                              ar6000_credit_init,
341                              servicepriority,
342                              5);
343
344     return A_OK;
345 }
346