1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /****************************************************************************** |
3 | * |
4 | * (C)Copyright 1998,1999 SysKonnect, |
5 | * a business unit of Schneider & Koch & Co. Datensysteme GmbH. |
6 | * |
7 | * See the file "skfddi.c" for further information. |
8 | * |
9 | * The information in this file is provided "AS IS" without warranty. |
10 | * |
11 | ******************************************************************************/ |
12 | |
13 | /* |
14 | SMT timer |
15 | */ |
16 | |
17 | #include "h/types.h" |
18 | #include "h/fddi.h" |
19 | #include "h/smc.h" |
20 | |
21 | static void timer_done(struct s_smc *smc, int restart); |
22 | |
23 | void smt_timer_init(struct s_smc *smc) |
24 | { |
25 | smc->t.st_queue = NULL; |
26 | smc->t.st_fast.tm_active = FALSE ; |
27 | smc->t.st_fast.tm_next = NULL; |
28 | hwt_init(smc) ; |
29 | } |
30 | |
31 | void smt_timer_stop(struct s_smc *smc, struct smt_timer *timer) |
32 | { |
33 | struct smt_timer **prev ; |
34 | struct smt_timer *tm ; |
35 | |
36 | /* |
37 | * remove timer from queue |
38 | */ |
39 | timer->tm_active = FALSE ; |
40 | if (smc->t.st_queue == timer && !timer->tm_next) { |
41 | hwt_stop(smc) ; |
42 | } |
43 | for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { |
44 | if (tm == timer) { |
45 | *prev = tm->tm_next ; |
46 | if (tm->tm_next) { |
47 | tm->tm_next->tm_delta += tm->tm_delta ; |
48 | } |
49 | return ; |
50 | } |
51 | } |
52 | } |
53 | |
54 | void smt_timer_start(struct s_smc *smc, struct smt_timer *timer, u_long time, |
55 | u_long token) |
56 | { |
57 | struct smt_timer **prev ; |
58 | struct smt_timer *tm ; |
59 | u_long delta = 0 ; |
60 | |
61 | time /= 16 ; /* input is uS, clock ticks are 16uS */ |
62 | if (!time) |
63 | time = 1 ; |
64 | smt_timer_stop(smc,timer) ; |
65 | timer->tm_smc = smc ; |
66 | timer->tm_token = token ; |
67 | timer->tm_active = TRUE ; |
68 | if (!smc->t.st_queue) { |
69 | smc->t.st_queue = timer ; |
70 | timer->tm_next = NULL; |
71 | timer->tm_delta = time ; |
72 | hwt_start(smc,time) ; |
73 | return ; |
74 | } |
75 | /* |
76 | * timer correction |
77 | */ |
78 | timer_done(smc,restart: 0) ; |
79 | |
80 | /* |
81 | * find position in queue |
82 | */ |
83 | delta = 0 ; |
84 | for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { |
85 | if (delta + tm->tm_delta > time) { |
86 | break ; |
87 | } |
88 | delta += tm->tm_delta ; |
89 | } |
90 | /* insert in queue */ |
91 | *prev = timer ; |
92 | timer->tm_next = tm ; |
93 | timer->tm_delta = time - delta ; |
94 | if (tm) |
95 | tm->tm_delta -= timer->tm_delta ; |
96 | /* |
97 | * start new with first |
98 | */ |
99 | hwt_start(smc,time: smc->t.st_queue->tm_delta) ; |
100 | } |
101 | |
102 | void smt_force_irq(struct s_smc *smc) |
103 | { |
104 | smt_timer_start(smc,timer: &smc->t.st_fast,time: 32L, EV_TOKEN(EVENT_SMT,SM_FAST)); |
105 | } |
106 | |
107 | void smt_timer_done(struct s_smc *smc) |
108 | { |
109 | timer_done(smc,restart: 1) ; |
110 | } |
111 | |
112 | static void timer_done(struct s_smc *smc, int restart) |
113 | { |
114 | u_long delta ; |
115 | struct smt_timer *tm ; |
116 | struct smt_timer *next ; |
117 | struct smt_timer **last ; |
118 | int done = 0 ; |
119 | |
120 | delta = hwt_read(smc) ; |
121 | last = &smc->t.st_queue ; |
122 | tm = smc->t.st_queue ; |
123 | while (tm && !done) { |
124 | if (delta >= tm->tm_delta) { |
125 | tm->tm_active = FALSE ; |
126 | delta -= tm->tm_delta ; |
127 | last = &tm->tm_next ; |
128 | tm = tm->tm_next ; |
129 | } |
130 | else { |
131 | tm->tm_delta -= delta ; |
132 | delta = 0 ; |
133 | done = 1 ; |
134 | } |
135 | } |
136 | *last = NULL; |
137 | next = smc->t.st_queue ; |
138 | smc->t.st_queue = tm ; |
139 | |
140 | for ( tm = next ; tm ; tm = next) { |
141 | next = tm->tm_next ; |
142 | timer_event(smc,token: tm->tm_token) ; |
143 | } |
144 | |
145 | if (restart && smc->t.st_queue) |
146 | hwt_start(smc,time: smc->t.st_queue->tm_delta) ; |
147 | } |
148 | |
149 | |