Linux 2.6.31-rc6
[linux-2.6] / 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 static void timer_done(struct s_smc *smc, int restart);
30
31 void smt_timer_init(struct s_smc *smc)
32 {
33         smc->t.st_queue = NULL;
34         smc->t.st_fast.tm_active = FALSE ;
35         smc->t.st_fast.tm_next = NULL;
36         hwt_init(smc) ;
37 }
38
39 void smt_timer_stop(struct s_smc *smc, struct smt_timer *timer)
40 {
41         struct smt_timer        **prev ;
42         struct smt_timer        *tm ;
43
44         /*
45          * remove timer from queue
46          */
47         timer->tm_active = FALSE ;
48         if (smc->t.st_queue == timer && !timer->tm_next) {
49                 hwt_stop(smc) ;
50         }
51         for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
52                 if (tm == timer) {
53                         *prev = tm->tm_next ;
54                         if (tm->tm_next) {
55                                 tm->tm_next->tm_delta += tm->tm_delta ;
56                         }
57                         return ;
58                 }
59         }
60 }
61
62 void smt_timer_start(struct s_smc *smc, struct smt_timer *timer, u_long time,
63                      u_long token)
64 {
65         struct smt_timer        **prev ;
66         struct smt_timer        *tm ;
67         u_long                  delta = 0 ;
68
69         time /= 16 ;            /* input is uS, clock ticks are 16uS */
70         if (!time)
71                 time = 1 ;
72         smt_timer_stop(smc,timer) ;
73         timer->tm_smc = smc ;
74         timer->tm_token = token ;
75         timer->tm_active = TRUE ;
76         if (!smc->t.st_queue) {
77                 smc->t.st_queue = timer ;
78                 timer->tm_next = NULL;
79                 timer->tm_delta = time ;
80                 hwt_start(smc,time) ;
81                 return ;
82         }
83         /*
84          * timer correction
85          */
86         timer_done(smc,0) ;
87
88         /*
89          * find position in queue
90          */
91         delta = 0 ;
92         for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) {
93                 if (delta + tm->tm_delta > time) {
94                         break ;
95                 }
96                 delta += tm->tm_delta ;
97         }
98         /* insert in queue */
99         *prev = timer ;
100         timer->tm_next = tm ;
101         timer->tm_delta = time - delta ;
102         if (tm)
103                 tm->tm_delta -= timer->tm_delta ;
104         /*
105          * start new with first
106          */
107         hwt_start(smc,smc->t.st_queue->tm_delta) ;
108 }
109
110 void smt_force_irq(struct s_smc *smc)
111 {
112         smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST)); 
113 }
114
115 void smt_timer_done(struct s_smc *smc)
116 {
117         timer_done(smc,1) ;
118 }
119
120 static void timer_done(struct s_smc *smc, int restart)
121 {
122         u_long                  delta ;
123         struct smt_timer        *tm ;
124         struct smt_timer        *next ;
125         struct smt_timer        **last ;
126         int                     done = 0 ;
127
128         delta = hwt_read(smc) ;
129         last = &smc->t.st_queue ;
130         tm = smc->t.st_queue ;
131         while (tm && !done) {
132                 if (delta >= tm->tm_delta) {
133                         tm->tm_active = FALSE ;
134                         delta -= tm->tm_delta ;
135                         last = &tm->tm_next ;
136                         tm = tm->tm_next ;
137                 }
138                 else {
139                         tm->tm_delta -= delta ;
140                         delta = 0 ;
141                         done = 1 ;
142                 }
143         }
144         *last = NULL;
145         next = smc->t.st_queue ;
146         smc->t.st_queue = tm ;
147
148         for ( tm = next ; tm ; tm = next) {
149                 next = tm->tm_next ;
150                 timer_event(smc,tm->tm_token) ;
151         }
152
153         if (restart && smc->t.st_queue)
154                 hwt_start(smc,smc->t.st_queue->tm_delta) ;
155 }
156