Merge branch 'upstream' of git://ftp.linux-mips.org/pub/scm/upstream-linus
[linux-2.6] / drivers / net / sk98lin / sktimer.c
1 /******************************************************************************
2  *
3  * Name:        sktimer.c
4  * Project:     Gigabit Ethernet Adapters, Event Scheduler Module
5  * Version:     $Revision: 1.14 $
6  * Date:        $Date: 2003/09/16 13:46:51 $
7  * Purpose:     High level timer functions.
8  *
9  ******************************************************************************/
10
11 /******************************************************************************
12  *
13  *      (C)Copyright 1998-2002 SysKonnect GmbH.
14  *      (C)Copyright 2002-2003 Marvell.
15  *
16  *      This program is free software; you can redistribute it and/or modify
17  *      it under the terms of the GNU General Public License as published by
18  *      the Free Software Foundation; either version 2 of the License, or
19  *      (at your option) any later version.
20  *
21  *      The information in this file is provided "AS IS" without warranty.
22  *
23  ******************************************************************************/
24
25
26 /*
27  *      Event queue and dispatcher
28  */
29 #if (defined(DEBUG) || ((!defined(LINT)) && (!defined(SK_SLIM))))
30 static const char SysKonnectFileId[] =
31         "@(#) $Id: sktimer.c,v 1.14 2003/09/16 13:46:51 rschmidt Exp $ (C) Marvell.";
32 #endif
33
34 #include "h/skdrv1st.h"         /* Driver Specific Definitions */
35 #include "h/skdrv2nd.h"         /* Adapter Control- and Driver specific Def. */
36
37 #ifdef __C2MAN__
38 /*
39         Event queue management.
40
41         General Description:
42
43  */
44 intro()
45 {}
46 #endif
47
48
49 /* Forward declaration */
50 static void timer_done(SK_AC *pAC,SK_IOC Ioc,int Restart);
51
52
53 /*
54  * Inits the software timer
55  *
56  * needs to be called during Init level 1.
57  */
58 void    SkTimerInit(
59 SK_AC   *pAC,           /* Adapters context */
60 SK_IOC  Ioc,            /* IoContext */
61 int             Level)          /* Init Level */
62 {
63         switch (Level) {
64         case SK_INIT_DATA:
65                 pAC->Tim.StQueue = NULL;
66                 break;
67         case SK_INIT_IO:
68                 SkHwtInit(pAC, Ioc);
69                 SkTimerDone(pAC, Ioc);
70                 break;
71         default:
72                 break;
73         }
74 }
75
76 /*
77  * Stops a high level timer
78  * - If a timer is not in the queue the function returns normally, too.
79  */
80 void    SkTimerStop(
81 SK_AC           *pAC,           /* Adapters context */
82 SK_IOC          Ioc,            /* IoContext */
83 SK_TIMER        *pTimer)        /* Timer Pointer to be started */
84 {
85         SK_TIMER        **ppTimPrev;
86         SK_TIMER        *pTm;
87
88         /*
89          * remove timer from queue
90          */
91         pTimer->TmActive = SK_FALSE;
92         
93         if (pAC->Tim.StQueue == pTimer && !pTimer->TmNext) {
94                 SkHwtStop(pAC, Ioc);
95         }
96         
97         for (ppTimPrev = &pAC->Tim.StQueue; (pTm = *ppTimPrev);
98                 ppTimPrev = &pTm->TmNext ) {
99                 
100                 if (pTm == pTimer) {
101                         /*
102                          * Timer found in queue
103                          * - dequeue it and
104                          * - correct delta of the next timer
105                          */
106                         *ppTimPrev = pTm->TmNext;
107
108                         if (pTm->TmNext) {
109                                 /* correct delta of next timer in queue */
110                                 pTm->TmNext->TmDelta += pTm->TmDelta;
111                         }
112                         return;
113                 }
114         }
115 }
116
117 /*
118  * Start a high level software timer
119  */
120 void    SkTimerStart(
121 SK_AC           *pAC,           /* Adapters context */
122 SK_IOC          Ioc,            /* IoContext */
123 SK_TIMER        *pTimer,        /* Timer Pointer to be started */
124 SK_U32          Time,           /* Time value */
125 SK_U32          Class,          /* Event Class for this timer */
126 SK_U32          Event,          /* Event Value for this timer */
127 SK_EVPARA       Para)           /* Event Parameter for this timer */
128 {
129         SK_TIMER        **ppTimPrev;
130         SK_TIMER        *pTm;
131         SK_U32          Delta;
132
133         Time /= 16;             /* input is uS, clock ticks are 16uS */
134         
135         if (!Time)
136                 Time = 1;
137
138         SkTimerStop(pAC, Ioc, pTimer);
139
140         pTimer->TmClass = Class;
141         pTimer->TmEvent = Event;
142         pTimer->TmPara = Para;
143         pTimer->TmActive = SK_TRUE;
144
145         if (!pAC->Tim.StQueue) {
146                 /* First Timer to be started */
147                 pAC->Tim.StQueue = pTimer;
148                 pTimer->TmNext = NULL;
149                 pTimer->TmDelta = Time;
150                 
151                 SkHwtStart(pAC, Ioc, Time);
152                 
153                 return;
154         }
155
156         /*
157          * timer correction
158          */
159         timer_done(pAC, Ioc, 0);
160
161         /*
162          * find position in queue
163          */
164         Delta = 0;
165         for (ppTimPrev = &pAC->Tim.StQueue; (pTm = *ppTimPrev);
166                 ppTimPrev = &pTm->TmNext ) {
167                 
168                 if (Delta + pTm->TmDelta > Time) {
169                         /* Position found */
170                         /* Here the timer needs to be inserted. */
171                         break;
172                 }
173                 Delta += pTm->TmDelta;
174         }
175
176         /* insert in queue */
177         *ppTimPrev = pTimer;
178         pTimer->TmNext = pTm;
179         pTimer->TmDelta = Time - Delta;
180
181         if (pTm) {
182                 /* There is a next timer
183                  * -> correct its Delta value.
184                  */
185                 pTm->TmDelta -= pTimer->TmDelta;
186         }
187
188         /* restart with first */
189         SkHwtStart(pAC, Ioc, pAC->Tim.StQueue->TmDelta);
190 }
191
192
193 void    SkTimerDone(
194 SK_AC   *pAC,           /* Adapters context */
195 SK_IOC  Ioc)            /* IoContext */
196 {
197         timer_done(pAC, Ioc, 1);
198 }
199
200
201 static void     timer_done(
202 SK_AC   *pAC,           /* Adapters context */
203 SK_IOC  Ioc,            /* IoContext */
204 int             Restart)        /* Do we need to restart the Hardware timer ? */
205 {
206         SK_U32          Delta;
207         SK_TIMER        *pTm;
208         SK_TIMER        *pTComp;        /* Timer completed now now */
209         SK_TIMER        **ppLast;       /* Next field of Last timer to be deq */
210         int             Done = 0;
211
212         Delta = SkHwtRead(pAC, Ioc);
213         
214         ppLast = &pAC->Tim.StQueue;
215         pTm = pAC->Tim.StQueue;
216         while (pTm && !Done) {
217                 if (Delta >= pTm->TmDelta) {
218                         /* Timer ran out */
219                         pTm->TmActive = SK_FALSE;
220                         Delta -= pTm->TmDelta;
221                         ppLast = &pTm->TmNext;
222                         pTm = pTm->TmNext;
223                 }
224                 else {
225                         /* We found the first timer that did not run out */
226                         pTm->TmDelta -= Delta;
227                         Delta = 0;
228                         Done = 1;
229                 }
230         }
231         *ppLast = NULL;
232         /*
233          * pTm points to the first Timer that did not run out.
234          * StQueue points to the first Timer that run out.
235          */
236
237         for ( pTComp = pAC->Tim.StQueue; pTComp; pTComp = pTComp->TmNext) {
238                 SkEventQueue(pAC,pTComp->TmClass, pTComp->TmEvent, pTComp->TmPara);
239         }
240
241         /* Set head of timer queue to the first timer that did not run out */
242         pAC->Tim.StQueue = pTm;
243
244         if (Restart && pAC->Tim.StQueue) {
245                 /* Restart HW timer */
246                 SkHwtStart(pAC, Ioc, pAC->Tim.StQueue->TmDelta);
247         }
248 }
249
250 /* End of file */