Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / drivers / staging / ath6kl / miscdrv / credit_dist.c
1 //------------------------------------------------------------------------------
2 // <copyright file="credit_dist.c" company="Atheros">
3 //    Copyright (c) 2004-2010 Atheros Corporation.  All rights reserved.
4 // 
5 //
6 // Permission to use, copy, modify, and/or distribute this software for any
7 // purpose with or without fee is hereby granted, provided that the above
8 // copyright notice and this permission notice appear in all copies.
9 //
10 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 //
18 //
19 //------------------------------------------------------------------------------
20 //==============================================================================
21 // Author(s): ="Atheros"
22 //==============================================================================
23
24 #include "a_config.h"
25 #include "athdefs.h"
26 #include "a_types.h"
27 #include "a_osapi.h"
28 #define ATH_MODULE_NAME misc
29 #include "a_debug.h"
30 #include "htc_api.h"
31 #include "common_drv.h"
32
33 /********* CREDIT DISTRIBUTION FUNCTIONS ******************************************/
34
35 #define NO_VO_SERVICE 1 /* currently WMI only uses 3 data streams, so we leave VO service inactive */
36 #define CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS 1
37
38 #ifdef NO_VO_SERVICE
39 #define DATA_SVCS_USED 3
40 #else
41 #define DATA_SVCS_USED 4
42 #endif
43
44 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
45                                 HTC_ENDPOINT_CREDIT_DIST *pEPDistList);
46
47 static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
48                         HTC_ENDPOINT_CREDIT_DIST *pEPDistList);
49
50 /* reduce an ep's credits back to a set limit */
51 static INLINE void ReduceCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
52                                 HTC_ENDPOINT_CREDIT_DIST  *pEpDist,
53                                 int                       Limit)
54 {
55     int credits;
56
57         /* set the new limit */
58     pEpDist->TxCreditsAssigned = Limit;
59
60     if (pEpDist->TxCredits <= Limit) {
61         return;
62     }
63
64         /* figure out how much to take away */
65     credits = pEpDist->TxCredits - Limit;
66         /* take them away */
67     pEpDist->TxCredits -= credits;
68     pCredInfo->CurrentFreeCredits += credits;
69 }
70
71 /* give an endpoint some credits from the free credit pool */
72 #define GiveCredits(pCredInfo,pEpDist,credits)      \
73 {                                                   \
74     (pEpDist)->TxCredits += (credits);              \
75     (pEpDist)->TxCreditsAssigned += (credits);      \
76     (pCredInfo)->CurrentFreeCredits -= (credits);   \
77 }
78
79
80 /* default credit init callback.
81  * This function is called in the context of HTCStart() to setup initial (application-specific)
82  * credit distributions */
83 static void ar6000_credit_init(void                     *Context,
84                                HTC_ENDPOINT_CREDIT_DIST *pEPList,
85                                int                      TotalCredits)
86 {
87     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
88     int                      count;
89     COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context;
90
91     pCredInfo->CurrentFreeCredits = TotalCredits;
92     pCredInfo->TotalAvailableCredits = TotalCredits;
93
94     pCurEpDist = pEPList;
95
96         /* run through the list and initialize */
97     while (pCurEpDist != NULL) {
98
99             /* set minimums for each endpoint */
100         pCurEpDist->TxCreditsMin = pCurEpDist->TxCreditsPerMaxMsg;
101
102 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
103  
104       if (TotalCredits > 4)
105       {
106           if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC)  || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)){
107                     /* assign at least min credits to lower than VO priority services */
108                 GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
109                     /* force active */
110                 SET_EP_ACTIVE(pCurEpDist);
111           }
112       }
113  
114 #endif
115
116         if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
117                 /* give control service some credits */
118             GiveCredits(pCredInfo,pCurEpDist,pCurEpDist->TxCreditsMin);
119                 /* control service is always marked active, it never goes inactive EVER */
120             SET_EP_ACTIVE(pCurEpDist);
121         } else if (pCurEpDist->ServiceID == WMI_DATA_BK_SVC) {
122                 /* this is the lowest priority data endpoint, save this off for easy access */
123             pCredInfo->pLowestPriEpDist = pCurEpDist;
124         }
125
126         /* Streams have to be created (explicit | implicit)for all kinds
127          * of traffic. BE endpoints are also inactive in the beginning.
128          * When BE traffic starts it creates implicit streams that
129          * redistributes credits.
130          */
131
132         /* note, all other endpoints have minimums set but are initially given NO credits.
133          * Credits will be distributed as traffic activity demands */
134         pCurEpDist = pCurEpDist->pNext;
135     }
136
137     if (pCredInfo->CurrentFreeCredits <= 0) {
138         AR_DEBUG_PRINTF(ATH_LOG_INF, ("Not enough credits (%d) to do credit distributions \n", TotalCredits));
139         A_ASSERT(FALSE);
140         return;
141     }
142
143         /* reset list */
144     pCurEpDist = pEPList;
145         /* now run through the list and set max operating credit limits for everyone */
146     while (pCurEpDist != NULL) {
147         if (pCurEpDist->ServiceID == WMI_CONTROL_SVC) {
148                 /* control service max is just 1 max message */
149             pCurEpDist->TxCreditsNorm = pCurEpDist->TxCreditsPerMaxMsg;
150         } else {
151                 /* for the remaining data endpoints, we assume that each TxCreditsPerMaxMsg are
152                  * the same.
153                  * We use a simple calculation here, we take the remaining credits and
154                  * determine how many max messages this can cover and then set each endpoint's
155                  * normal value equal to 3/4 this amount.
156                  * */
157             count = (pCredInfo->CurrentFreeCredits/pCurEpDist->TxCreditsPerMaxMsg) * pCurEpDist->TxCreditsPerMaxMsg;
158             count = (count * 3) >> 2;
159             count = max(count,pCurEpDist->TxCreditsPerMaxMsg);
160                 /* set normal */
161             pCurEpDist->TxCreditsNorm = count;
162
163         }
164         pCurEpDist = pCurEpDist->pNext;
165     }
166
167 }
168
169
170 /* default credit distribution callback
171  * This callback is invoked whenever endpoints require credit distributions.
172  * A lock is held while this function is invoked, this function shall NOT block.
173  * The pEPDistList is a list of distribution structures in prioritized order as
174  * defined by the call to the HTCSetCreditDistribution() api.
175  *
176  */
177 static void ar6000_credit_distribute(void                     *Context,
178                                      HTC_ENDPOINT_CREDIT_DIST *pEPDistList,
179                                      HTC_CREDIT_DIST_REASON   Reason)
180 {
181     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
182     COMMON_CREDIT_STATE_INFO *pCredInfo = (COMMON_CREDIT_STATE_INFO *)Context;
183
184     switch (Reason) {
185         case HTC_CREDIT_DIST_SEND_COMPLETE :
186             pCurEpDist = pEPDistList;
187                 /* we are given the start of the endpoint distribution list.
188                  * There may be one or more endpoints to service.
189                  * Run through the list and distribute credits */
190             while (pCurEpDist != NULL) {
191
192                 if (pCurEpDist->TxCreditsToDist > 0) {
193                         /* return the credits back to the endpoint */
194                     pCurEpDist->TxCredits += pCurEpDist->TxCreditsToDist;
195                         /* always zero out when we are done */
196                     pCurEpDist->TxCreditsToDist = 0;
197
198                     if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsAssigned) {
199                             /* reduce to the assigned limit, previous credit reductions
200                              * could have caused the limit to change */
201                         ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsAssigned);
202                     }
203
204                     if (pCurEpDist->TxCredits > pCurEpDist->TxCreditsNorm) {
205                             /* oversubscribed endpoints need to reduce back to normal */
206                         ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsNorm);
207                     }
208                 
209                     if (!IS_EP_ACTIVE(pCurEpDist)) {
210                             /* endpoint is inactive, now check for messages waiting for credits */
211                         if (pCurEpDist->TxQueueDepth == 0) {
212                                 /* EP is inactive and there are no pending messages, 
213                                  * reduce credits back to zero to recover credits */
214                             ReduceCredits(pCredInfo, pCurEpDist, 0);
215                         }
216                     }
217                 }
218
219                 pCurEpDist = pCurEpDist->pNext;
220             }
221
222             break;
223
224         case HTC_CREDIT_DIST_ACTIVITY_CHANGE :
225             RedistributeCredits(pCredInfo,pEPDistList);
226             break;
227         case HTC_CREDIT_DIST_SEEK_CREDITS :
228             SeekCredits(pCredInfo,pEPDistList);
229             break;
230         case HTC_DUMP_CREDIT_STATE :
231             AR_DEBUG_PRINTF(ATH_DEBUG_ERR, ("Credit Distribution, total : %d, free : %d\n",
232                                                                         pCredInfo->TotalAvailableCredits, pCredInfo->CurrentFreeCredits));
233             break;
234         default:
235             break;
236
237     }
238
239         /* sanity checks done after each distribution action */
240     A_ASSERT(pCredInfo->CurrentFreeCredits <= pCredInfo->TotalAvailableCredits);
241     A_ASSERT(pCredInfo->CurrentFreeCredits >= 0);
242
243 }
244
245 /* redistribute credits based on activity change */
246 static void RedistributeCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
247                                 HTC_ENDPOINT_CREDIT_DIST *pEPDistList)
248 {
249     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist = pEPDistList;
250
251         /* walk through the list and remove credits from inactive endpoints */
252     while (pCurEpDist != NULL) {
253
254 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
255
256         if ((pCurEpDist->ServiceID == WMI_DATA_BK_SVC)  || (pCurEpDist->ServiceID == WMI_DATA_BE_SVC)) {
257               /* force low priority streams to always be active to retain their minimum credit distribution */
258              SET_EP_ACTIVE(pCurEpDist);
259         }
260 #endif
261
262         if (pCurEpDist->ServiceID != WMI_CONTROL_SVC) {
263             if (!IS_EP_ACTIVE(pCurEpDist)) {
264                 if (pCurEpDist->TxQueueDepth == 0) {
265                         /* EP is inactive and there are no pending messages, reduce credits back to zero */
266                     ReduceCredits(pCredInfo, pCurEpDist, 0);
267                 } else {
268                         /* we cannot zero the credits assigned to this EP, but to keep
269                          * the credits available for these leftover packets, reduce to
270                          * a minimum */
271                     ReduceCredits(pCredInfo, pCurEpDist, pCurEpDist->TxCreditsMin);
272                 }
273             }
274         }
275
276         /* NOTE in the active case, we do not need to do anything further,
277          * when an EP goes active and needs credits, HTC will call into
278          * our distribution function using a reason code of HTC_CREDIT_DIST_SEEK_CREDITS  */
279
280         pCurEpDist = pCurEpDist->pNext;
281     }
282
283 }
284
285 /* HTC has an endpoint that needs credits, pEPDist is the endpoint in question */
286 static void SeekCredits(COMMON_CREDIT_STATE_INFO *pCredInfo,
287                         HTC_ENDPOINT_CREDIT_DIST *pEPDist)
288 {
289     HTC_ENDPOINT_CREDIT_DIST *pCurEpDist;
290     int                      credits = 0;
291     int                      need;
292
293     do {
294
295         if (pEPDist->ServiceID == WMI_CONTROL_SVC) {
296                 /* we never oversubscribe on the control service, this is not
297                  * a high performance path and the target never holds onto control
298                  * credits for too long */
299             break;
300         }
301
302 #ifdef CONFIG_GIVE_LOW_PRIORITY_STREAMS_MIN_CREDITS
303         if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
304             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
305                  /* limit VI service from oversubscribing */
306                  break;
307             }
308         }
309  
310         if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
311             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm)) {
312                  /* limit VO service from oversubscribing */
313                 break;
314             }
315         }
316 #else
317         if (pEPDist->ServiceID == WMI_DATA_VI_SVC) {
318             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
319                 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
320                  /* limit VI service from oversubscribing */
321                  /* at least one free credit will not be used by VI */
322                  break;
323             }
324         }
325  
326         if (pEPDist->ServiceID == WMI_DATA_VO_SVC) {
327             if ((pEPDist->TxCreditsAssigned >= pEPDist->TxCreditsNorm) ||
328                 (pCredInfo->CurrentFreeCredits <= pEPDist->TxCreditsPerMaxMsg)) {
329                  /* limit VO service from oversubscribing */
330                  /* at least one free credit will not be used by VO */
331                 break;
332             }
333         }
334 #endif
335
336         /* for all other services, we follow a simple algorithm of
337          * 1. checking the free pool for credits
338          * 2. checking lower priority endpoints for credits to take */
339
340             /* give what we can */
341         credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
342
343         if (credits >= pEPDist->TxCreditsSeek) {
344                 /* we found some to fullfill the seek request */
345             break;
346         }
347
348         /* we don't have enough in the free pool, try taking away from lower priority services
349          *
350          * The rule for taking away credits:
351          *   1. Only take from lower priority endpoints
352          *   2. Only take what is allocated above the minimum (never starve an endpoint completely)
353          *   3. Only take what you need.
354          *
355          * */
356
357             /* starting at the lowest priority */
358         pCurEpDist = pCredInfo->pLowestPriEpDist;
359
360             /* work backwards until we hit the endpoint again */
361         while (pCurEpDist != pEPDist) {
362                 /* calculate how many we need so far */
363             need = pEPDist->TxCreditsSeek - pCredInfo->CurrentFreeCredits;
364
365             if ((pCurEpDist->TxCreditsAssigned - need) >= pCurEpDist->TxCreditsMin) {
366                     /* the current one has been allocated more than it's minimum and it
367                      * has enough credits assigned above it's minimum to fullfill our need
368                      * try to take away just enough to fullfill our need */
369                 ReduceCredits(pCredInfo,
370                               pCurEpDist,
371                               pCurEpDist->TxCreditsAssigned - need);
372
373                 if (pCredInfo->CurrentFreeCredits >= pEPDist->TxCreditsSeek) {
374                         /* we have enough */
375                     break;
376                 }
377             }
378
379             pCurEpDist = pCurEpDist->pPrev;
380         }
381
382             /* return what we can get */
383         credits = min(pCredInfo->CurrentFreeCredits,pEPDist->TxCreditsSeek);
384
385     } while (FALSE);
386
387         /* did we find some credits? */
388     if (credits) {
389             /* give what we can */
390         GiveCredits(pCredInfo, pEPDist, credits);
391     }
392
393 }
394
395 /* initialize and setup credit distribution */
396 A_STATUS ar6000_setup_credit_dist(HTC_HANDLE HTCHandle, COMMON_CREDIT_STATE_INFO *pCredInfo)
397 {
398     HTC_SERVICE_ID servicepriority[5];
399
400     A_MEMZERO(pCredInfo,sizeof(COMMON_CREDIT_STATE_INFO));
401
402     servicepriority[0] = WMI_CONTROL_SVC;  /* highest */
403     servicepriority[1] = WMI_DATA_VO_SVC;
404     servicepriority[2] = WMI_DATA_VI_SVC;
405     servicepriority[3] = WMI_DATA_BE_SVC;
406     servicepriority[4] = WMI_DATA_BK_SVC; /* lowest */
407
408         /* set callbacks and priority list */
409     HTCSetCreditDistribution(HTCHandle,
410                              pCredInfo,
411                              ar6000_credit_distribute,
412                              ar6000_credit_init,
413                              servicepriority,
414                              5);
415
416     return A_OK;
417 }
418