ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / net / skfp / smttimer.c
1 /******************************************************************************
2  *
3  *      (C)Copyright 1998,1999 SysKonnect,
4  *      a business unit of Schneider & Koch & Co. Datensysteme GmbH.
5  *
6  *      See the file "skfddi.c" for further information.
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 as published by
10  *      the Free Software Foundation; either version 2 of the License, or
11  *      (at your option) any later version.
12  *
13  *      The information in this file is provided "AS IS" without warranty.
14  *
15  ******************************************************************************/
16
17 /*
18         SMT timer
19 */
20
21 #include "h/types.h"
22 #include "h/fddi.h"
23 #include "h/smc.h"
24
25 #ifndef lint
26 static const char ID_sccs[] = "@(#)smttimer.c   2.4 97/08/04 (C) SK " ;
27 #endif
28
29 /*
30  * external function declarations
31  */
32 extern u_long hwt_read() ;
33 extern void hwt_stop() ;
34 extern void hwt_start() ;
35
36 static void timer_done() ;
37
38
39 void smt_timer_init(smc)
40 struct s_smc *smc ;
41 {
42         smc->t.st_queue = 0 ;
43         smc->t.st_fast.tm_active = FALSE ;
44         smc->t.st_fast.tm_next = 0 ;
45         hwt_init(smc) ;
46 }
47
48 void smt_timer_stop(smc,timer)
49 struct s_smc *smc ;
50 struct smt_timer *timer ;
51 {
52         struct smt_timer        **prev ;
53         struct smt_timer        *tm ;
54
55         /*
56          * remove timer from queue
57          */
58         timer->tm_active = FALSE ;
59         if (smc->t.st_queue == timer && !timer->tm_next) {
60                 hwt_stop(smc) ;
61         }
62         for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
63                 if (tm == timer) {
64                         *prev = tm->tm_next ;
65                         if (tm->tm_next) {
66                                 tm->tm_next->tm_delta += tm->tm_delta ;
67                         }
68                         return ;
69                 }
70         }
71 }
72
73 void smt_timer_start(smc,timer,time,token)
74 struct s_smc *smc ;
75 struct smt_timer *timer ;
76 u_long          time ;
77 u_long          token ;
78 {
79         struct smt_timer        **prev ;
80         struct smt_timer        *tm ;
81         u_long                  delta = 0 ;
82
83         time /= 16 ;            /* input is uS, clock ticks are 16uS */
84         if (!time)
85                 time = 1 ;
86         smt_timer_stop(smc,timer) ;
87         timer->tm_smc = smc ;
88         timer->tm_token = token ;
89         timer->tm_active = TRUE ;
90         if (!smc->t.st_queue) {
91                 smc->t.st_queue = timer ;
92                 timer->tm_next = 0 ;
93                 timer->tm_delta = time ;
94                 hwt_start(smc,time) ;
95                 return ;
96         }
97         /*
98          * timer correction
99          */
100         timer_done(smc,0) ;
101
102         /*
103          * find position in queue
104          */
105         delta = 0 ;
106         for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
107                 if (delta + tm->tm_delta > time) {
108                         break ;
109                 }
110                 delta += tm->tm_delta ;
111         }
112         /* insert in queue */
113         *prev = timer ;
114         timer->tm_next = tm ;
115         timer->tm_delta = time - delta ;
116         if (tm)
117                 tm->tm_delta -= timer->tm_delta ;
118         /*
119          * start new with first
120          */
121         hwt_start(smc,smc->t.st_queue->tm_delta) ;
122 }
123
124 void smt_force_irq(smc)
125 struct s_smc *smc ;
126 {
127         smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST)); 
128 }
129
130 void smt_timer_done(smc)
131 struct s_smc *smc ;
132 {
133         timer_done(smc,1) ;
134 }
135
136 static void timer_done(smc,restart)
137 struct s_smc *smc ;
138 int restart ;
139 {
140         u_long                  delta ;
141         struct smt_timer        *tm ;
142         struct smt_timer        *next ;
143         struct smt_timer        **last ;
144         int                     done = 0 ;
145
146         delta = hwt_read(smc) ;
147         last = &smc->t.st_queue ;
148         tm = smc->t.st_queue ;
149         while (tm && !done) {
150                 if (delta >= tm->tm_delta) {
151                         tm->tm_active = FALSE ;
152                         delta -= tm->tm_delta ;
153                         last = &tm->tm_next ;
154                         tm = tm->tm_next ;
155                 }
156                 else {
157                         tm->tm_delta -= delta ;
158                         delta = 0 ;
159                         done = 1 ;
160                 }
161         }
162         *last = 0 ;
163         next = smc->t.st_queue ;
164         smc->t.st_queue = tm ;
165
166         for ( tm = next ; tm ; tm = next) {
167                 next = tm->tm_next ;
168                 timer_event(smc,tm->tm_token) ;
169         }
170
171         if (restart && smc->t.st_queue)
172                 hwt_start(smc,smc->t.st_queue->tm_delta) ;
173 }