firewire: fw-ohci: fix "scheduling while atomic"
[linux-2.6] / drivers / net / skfp / hwt.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  * Timer Driver for FBI board (timer chip 82C54)
19  */
20
21 /*
22  * Modifications:
23  *
24  *      28-Jun-1994 sw  Edit v1.6.
25  *                      MCA: Added support for the SK-NET FDDI-FM2 adapter. The
26  *                       following functions have been added(+) or modified(*):
27  *                       hwt_start(*), hwt_stop(*), hwt_restart(*), hwt_read(*)
28  */
29
30 #include "h/types.h"
31 #include "h/fddi.h"
32 #include "h/smc.h"
33
34 #ifndef lint
35 static const char ID_sccs[] = "@(#)hwt.c        1.13 97/04/23 (C) SK " ;
36 #endif
37
38 /*
39  * Prototypes of local functions.
40  */
41 /* 28-Jun-1994 sw - Note: hwt_restart() is also used in module 'drvfbi.c'. */
42 /*static void hwt_restart() ; */
43
44 /************************
45  *
46  *      hwt_start
47  *
48  *      Start hardware timer (clock ticks are 16us).
49  *
50  *      void hwt_start(
51  *              struct s_smc *smc,
52  *              u_long time) ;
53  * In
54  *      smc - A pointer to the SMT Context structure.
55  *
56  *      time - The time in units of 16us to load the timer with.
57  * Out
58  *      Nothing.
59  *
60  ************************/
61 #define HWT_MAX (65000)
62
63 void hwt_start(struct s_smc *smc, u_long time)
64 {
65         u_short cnt ;
66
67         if (time > HWT_MAX)
68                 time = HWT_MAX ;
69
70         smc->hw.t_start = time ;
71         smc->hw.t_stop = 0L ;
72
73         cnt = (u_short)time ;
74         /*
75          * if time < 16 us
76          *      time = 16 us
77          */
78         if (!cnt)
79                 cnt++ ;
80 #ifndef PCI
81         /*
82          * 6.25MHz -> CLK0 : T0 (cnt0 = 16us)   -> OUT0
83          *    OUT0 -> CLK1 : T1 (cnt1)  OUT1    -> ISRA(IS_TIMINT)
84          */
85         OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ; /* counter 1, mode 0 */
86         OUT_82c54_TIMER(1,cnt & 0xff) ;         /* LSB */
87         OUT_82c54_TIMER(1,(cnt>>8) & 0xff) ;    /* MSB */
88         /*
89          * start timer by switching counter 0 to mode 3
90          *      T0 resolution 16 us (CLK0=0.16us)
91          */
92         OUT_82c54_TIMER(3,0<<6 | 3<<4 | 3<<1) ; /* counter 0, mode 3 */
93         OUT_82c54_TIMER(0,100) ;                /* LSB */
94         OUT_82c54_TIMER(0,0) ;                  /* MSB */
95 #else   /* PCI */
96         outpd(ADDR(B2_TI_INI), (u_long) cnt * 200) ;    /* Load timer value. */
97         outpw(ADDR(B2_TI_CRTL), TIM_START) ;            /* Start timer. */
98 #endif  /* PCI */
99         smc->hw.timer_activ = TRUE ;
100 }
101
102 /************************
103  *
104  *      hwt_stop
105  *
106  *      Stop hardware timer.
107  *
108  *      void hwt_stop(
109  *              struct s_smc *smc) ;
110  * In
111  *      smc - A pointer to the SMT Context structure.
112  * Out
113  *      Nothing.
114  *
115  ************************/
116 void hwt_stop(struct s_smc *smc)
117 {
118 #ifndef PCI
119         /* stop counter 0 by switching to mode 0 */
120         OUT_82c54_TIMER(3,0<<6 | 3<<4 | 0<<1) ; /* counter 0, mode 0 */
121         OUT_82c54_TIMER(0,0) ;                  /* LSB */
122         OUT_82c54_TIMER(0,0) ;                  /* MSB */
123 #else   /* PCI */
124         outpw(ADDR(B2_TI_CRTL), TIM_STOP) ;
125         outpw(ADDR(B2_TI_CRTL), TIM_CL_IRQ) ;
126 #endif  /* PCI */
127
128         smc->hw.timer_activ = FALSE ;
129 }
130
131 /************************
132  *
133  *      hwt_init
134  *
135  *      Initialize hardware timer.
136  *
137  *      void hwt_init(
138  *              struct s_smc *smc) ;
139  * In
140  *      smc - A pointer to the SMT Context structure.
141  * Out
142  *      Nothing.
143  *
144  ************************/
145 void hwt_init(struct s_smc *smc)
146 {
147         smc->hw.t_start = 0 ;
148         smc->hw.t_stop  = 0 ;
149         smc->hw.timer_activ = FALSE ;
150
151         hwt_restart(smc) ;
152 }
153
154 /************************
155  *
156  *      hwt_restart
157  *
158  *      Clear timer interrupt.
159  *
160  *      void hwt_restart(
161  *              struct s_smc *smc) ;
162  * In
163  *      smc - A pointer to the SMT Context structure.
164  * Out
165  *      Nothing.
166  *
167  ************************/
168 void hwt_restart(struct s_smc *smc)
169 {
170         hwt_stop(smc) ;
171 #ifndef PCI
172         OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ; /* counter 1, mode 0 */
173         OUT_82c54_TIMER(1,1 ) ;                 /* LSB */
174         OUT_82c54_TIMER(1,0 ) ;                 /* MSB */
175 #endif
176 }
177
178 /************************
179  *
180  *      hwt_read
181  *
182  *      Stop hardware timer and read time elapsed since last start.
183  *
184  *      u_long hwt_read(smc) ;
185  * In
186  *      smc - A pointer to the SMT Context structure.
187  * Out
188  *      The elapsed time since last start in units of 16us.
189  *
190  ************************/
191 u_long hwt_read(struct s_smc *smc)
192 {
193         u_short tr ;
194 #ifndef PCI
195         u_short is ;
196 #else
197         u_long  is ;
198 #endif
199
200         if (smc->hw.timer_activ) {
201                 hwt_stop(smc) ;
202 #ifndef PCI
203                 OUT_82c54_TIMER(3,1<<6) ;       /* latch command */
204                 tr = IN_82c54_TIMER(1) & 0xff ;
205                 tr += (IN_82c54_TIMER(1) & 0xff)<<8 ;
206 #else   /* PCI */
207                 tr = (u_short)((inpd(ADDR(B2_TI_VAL))/200) & 0xffff) ;
208 #endif  /* PCI */
209                 is = GET_ISR() ;
210                 /* Check if timer expired (or wraparound). */
211                 if ((tr > smc->hw.t_start) || (is & IS_TIMINT)) {
212                         hwt_restart(smc) ;
213                         smc->hw.t_stop = smc->hw.t_start ;
214                 }
215                 else
216                         smc->hw.t_stop = smc->hw.t_start - tr ;
217         }
218         return (smc->hw.t_stop) ;
219 }
220
221 #ifdef  PCI
222 /************************
223  *
224  *      hwt_quick_read
225  *
226  *      Stop hardware timer and read timer value and start the timer again.
227  *
228  *      u_long hwt_read(smc) ;
229  * In
230  *      smc - A pointer to the SMT Context structure.
231  * Out
232  *      current timer value in units of 80ns.
233  *
234  ************************/
235 u_long hwt_quick_read(struct s_smc *smc)
236 {
237         u_long interval ;
238         u_long time ;
239
240         interval = inpd(ADDR(B2_TI_INI)) ;
241         outpw(ADDR(B2_TI_CRTL), TIM_STOP) ;
242         time = inpd(ADDR(B2_TI_VAL)) ;
243         outpd(ADDR(B2_TI_INI),time) ;
244         outpw(ADDR(B2_TI_CRTL), TIM_START) ;
245         outpd(ADDR(B2_TI_INI),interval) ;
246
247         return(time) ;
248 }
249
250 /************************
251  *
252  *      hwt_wait_time(smc,start,duration)
253  *
254  *      This function returnes after the amount of time is elapsed
255  *      since the start time.
256  * 
257  * para start           start time
258  *      duration        time to wait
259  *
260  * NOTE: The fuction will return immediately, if the timer is not 
261  *       started
262  ************************/
263 void hwt_wait_time(struct s_smc *smc, u_long start, long int duration)
264 {
265         long    diff ;
266         long    interval ;
267         int     wrapped ;
268
269         /*
270          * check if timer is running
271          */
272         if (smc->hw.timer_activ == FALSE ||
273                 hwt_quick_read(smc) == hwt_quick_read(smc)) {
274                 return ;
275         }
276
277         interval = inpd(ADDR(B2_TI_INI)) ;
278         if (interval > duration) {
279                 do {
280                         diff = (long)(start - hwt_quick_read(smc)) ;
281                         if (diff < 0) {
282                                 diff += interval ;
283                         }
284                 } while (diff <= duration) ;
285         }
286         else {
287                 diff = interval ;
288                 wrapped = 0 ;
289                 do {
290                         if (!wrapped) {
291                                 if (hwt_quick_read(smc) >= start) {
292                                         diff += interval ;
293                                         wrapped = 1 ;
294                                 }
295                         }
296                         else {
297                                 if (hwt_quick_read(smc) < start) {
298                                         wrapped = 0 ;
299                                 }
300                         }
301                 } while (diff <= duration) ;
302         }
303 }
304 #endif
305