Merge branch 'for-2.6.31' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
[pandora-kernel.git] / drivers / staging / comedi / drivers / 8253.h
1 /*
2     comedi/drivers/8253.h
3     Header file for 8253
4
5     COMEDI - Linux Control and Measurement Device Interface
6     Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 */
23
24 #ifndef _8253_H
25 #define _8253_H
26
27 #include "../comedi.h"
28
29 #define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
30
31 static inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
32         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
33         int round_mode)
34 {
35         int divider;
36         int div1, div2;
37         int div1_glb, div2_glb, ns_glb;
38         int div1_lub, div2_lub, ns_lub;
39         int ns;
40
41         divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
42
43         /* find 2 integers 1<={x,y}<=65536 such that x*y is
44            close to divider */
45
46         div1_lub = div2_lub = 0;
47         div1_glb = div2_glb = 0;
48
49         ns_glb = 0;
50         ns_lub = 0xffffffff;
51
52         div2 = 0x10000;
53         for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
54                 div2 = divider / div1;
55
56                 ns = i8253_osc_base * div1 * div2;
57                 if (ns <= *nanosec && ns > ns_glb) {
58                         ns_glb = ns;
59                         div1_glb = div1;
60                         div2_glb = div2;
61                 }
62
63                 div2++;
64                 if (div2 <= 65536) {
65                         ns = i8253_osc_base * div1 * div2;
66                         if (ns > *nanosec && ns < ns_lub) {
67                                 ns_lub = ns;
68                                 div1_lub = div1;
69                                 div2_lub = div2;
70                         }
71                 }
72         }
73
74         *nanosec = div1_lub * div2_lub * i8253_osc_base;
75         *d1 = div1_lub & 0xffff;
76         *d2 = div2_lub & 0xffff;
77         return;
78 }
79
80 static inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
81         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
82         int round_mode)
83 {
84         int div1, div2;
85         int base;
86
87         for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
88                 base = i8253_osc_base * div1;
89                 round_mode &= TRIG_ROUND_MASK;
90                 switch (round_mode) {
91                 case TRIG_ROUND_NEAREST:
92                 default:
93                         div2 = (*nanosec + base / 2) / base;
94                         break;
95                 case TRIG_ROUND_DOWN:
96                         div2 = (*nanosec) / base;
97                         break;
98                 case TRIG_ROUND_UP:
99                         div2 = (*nanosec + base - 1) / base;
100                         break;
101                 }
102                 if (div2 < 2)
103                         div2 = 2;
104                 if (div2 <= 65536) {
105                         *nanosec = div2 * base;
106                         *d1 = div1 & 0xffff;
107                         *d2 = div2 & 0xffff;
108                         return;
109                 }
110         }
111
112         /* shouldn't get here */
113         div1 = 0x10000;
114         div2 = 0x10000;
115         *nanosec = div1 * div2 * i8253_osc_base;
116         *d1 = div1 & 0xffff;
117         *d2 = div2 & 0xffff;
118 }
119
120 static inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
121         unsigned int *d1, unsigned int *d2, unsigned int *nanosec,
122         int round_mode)
123 {
124         unsigned int divider;
125         unsigned int div1, div2;
126         unsigned int div1_glb, div2_glb, ns_glb;
127         unsigned int div1_lub, div2_lub, ns_lub;
128         unsigned int ns;
129         unsigned int start;
130         unsigned int ns_low, ns_high;
131         static const unsigned int max_count = 0x10000;
132         /* exit early if everything is already correct (this can save time
133          * since this function may be called repeatedly during command tests
134          * and execution) */
135         div1 = *d1 ? *d1 : max_count;
136         div2 = *d2 ? *d2 : max_count;
137         divider = div1 * div2;
138         if (div1 * div2 * i8253_osc_base == *nanosec &&
139                 div1 > 1 && div1 <= max_count &&
140                 div2 > 1 && div2 <= max_count &&
141                 /* check for overflow */
142                 divider > div1 && divider > div2 &&
143                 divider * i8253_osc_base > divider &&
144                 divider * i8253_osc_base > i8253_osc_base) {
145                 return;
146         }
147
148         divider = *nanosec / i8253_osc_base;
149
150         div1_lub = div2_lub = 0;
151         div1_glb = div2_glb = 0;
152
153         ns_glb = 0;
154         ns_lub = 0xffffffff;
155
156         div2 = max_count;
157         start = divider / div2;
158         if (start < 2)
159                 start = 2;
160         for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
161                 div1++) {
162                 for (div2 = divider / div1;
163                         div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
164                         div2++) {
165                         ns = i8253_osc_base * div1 * div2;
166                         if (ns <= *nanosec && ns > ns_glb) {
167                                 ns_glb = ns;
168                                 div1_glb = div1;
169                                 div2_glb = div2;
170                         }
171                         if (ns >= *nanosec && ns < ns_lub) {
172                                 ns_lub = ns;
173                                 div1_lub = div1;
174                                 div2_lub = div2;
175                         }
176                 }
177         }
178
179         round_mode &= TRIG_ROUND_MASK;
180         switch (round_mode) {
181         case TRIG_ROUND_NEAREST:
182         default:
183                 ns_high = div1_lub * div2_lub * i8253_osc_base;
184                 ns_low = div1_glb * div2_glb * i8253_osc_base;
185                 if (ns_high - *nanosec < *nanosec - ns_low) {
186                         div1 = div1_lub;
187                         div2 = div2_lub;
188                 } else {
189                         div1 = div1_glb;
190                         div2 = div2_glb;
191                 }
192                 break;
193         case TRIG_ROUND_UP:
194                 div1 = div1_lub;
195                 div2 = div2_lub;
196                 break;
197         case TRIG_ROUND_DOWN:
198                 div1 = div1_glb;
199                 div2 = div2_glb;
200                 break;
201         }
202
203         *nanosec = div1 * div2 * i8253_osc_base;
204         *d1 = div1 & 0xffff;    /*  masking is done since counter maps zero to 0x10000 */
205         *d2 = div2 & 0xffff;
206         return;
207 }
208
209 #ifndef CMDTEST
210 /* i8254_load programs 8254 counter chip.  It should also work for the 8253.
211  * base_address is the lowest io address for the chip (the address of counter 0).
212  * counter_number is the counter you want to load (0,1 or 2)
213  * count is the number to load into the counter.
214  *
215  * You probably want to use mode 2.
216  *
217  * Use i8254_mm_load() if you board uses memory-mapped io, it is
218  * the same as i8254_load() except it uses writeb() instead of outb().
219  *
220  * Neither i8254_load() or i8254_read() do their loading/reading
221  * atomically.  The 16 bit read/writes are performed with two successive
222  * 8 bit read/writes.  So if two parts of your driver do a load/read on
223  * the same counter, it may be necessary to protect these functions
224  * with a spinlock.
225  *
226  * FMH
227  */
228
229 #define i8254_control_reg       3
230
231 static inline int i8254_load(unsigned long base_address, unsigned int regshift,
232         unsigned int counter_number, unsigned int count, unsigned int mode)
233 {
234         unsigned int byte;
235
236         if (counter_number > 2)
237                 return -1;
238         if (count > 0xffff)
239                 return -1;
240         if (mode > 5)
241                 return -1;
242         if ((mode == 2 || mode == 3) && count == 1)
243                 return -1;
244
245         byte = counter_number << 6;
246         byte |= 0x30;           /*  load low then high byte */
247         byte |= (mode << 1);    /*  set counter mode */
248         outb(byte, base_address + (i8254_control_reg << regshift));
249         byte = count & 0xff;    /*  lsb of counter value */
250         outb(byte, base_address + (counter_number << regshift));
251         byte = (count >> 8) & 0xff;     /*  msb of counter value */
252         outb(byte, base_address + (counter_number << regshift));
253
254         return 0;
255 }
256
257 static inline int i8254_mm_load(void *base_address, unsigned int regshift,
258         unsigned int counter_number, unsigned int count, unsigned int mode)
259 {
260         unsigned int byte;
261
262         if (counter_number > 2)
263                 return -1;
264         if (count > 0xffff)
265                 return -1;
266         if (mode > 5)
267                 return -1;
268         if ((mode == 2 || mode == 3) && count == 1)
269                 return -1;
270
271         byte = counter_number << 6;
272         byte |= 0x30;           /*  load low then high byte */
273         byte |= (mode << 1);    /*  set counter mode */
274         writeb(byte, base_address + (i8254_control_reg << regshift));
275         byte = count & 0xff;    /*  lsb of counter value */
276         writeb(byte, base_address + (counter_number << regshift));
277         byte = (count >> 8) & 0xff;     /*  msb of counter value */
278         writeb(byte, base_address + (counter_number << regshift));
279
280         return 0;
281 }
282
283 /* Returns 16 bit counter value, should work for 8253 also.*/
284 static inline int i8254_read(unsigned long base_address, unsigned int regshift,
285         unsigned int counter_number)
286 {
287         unsigned int byte;
288         int ret;
289
290         if (counter_number > 2)
291                 return -1;
292
293         /*  latch counter */
294         byte = counter_number << 6;
295         outb(byte, base_address + (i8254_control_reg << regshift));
296
297         /*  read lsb */
298         ret = inb(base_address + (counter_number << regshift));
299         /*  read msb */
300         ret += inb(base_address + (counter_number << regshift)) << 8;
301
302         return ret;
303 }
304
305 static inline int i8254_mm_read(void *base_address, unsigned int regshift,
306         unsigned int counter_number)
307 {
308         unsigned int byte;
309         int ret;
310
311         if (counter_number > 2)
312                 return -1;
313
314         /*  latch counter */
315         byte = counter_number << 6;
316         writeb(byte, base_address + (i8254_control_reg << regshift));
317
318         /*  read lsb */
319         ret = readb(base_address + (counter_number << regshift));
320         /*  read msb */
321         ret += readb(base_address + (counter_number << regshift)) << 8;
322
323         return ret;
324 }
325
326 /* Loads 16 bit initial counter value, should work for 8253 also. */
327 static inline void i8254_write(unsigned long base_address,
328         unsigned int regshift, unsigned int counter_number, unsigned int count)
329 {
330         unsigned int byte;
331
332         if (counter_number > 2)
333                 return;
334
335         byte = count & 0xff;    /*  lsb of counter value */
336         outb(byte, base_address + (counter_number << regshift));
337         byte = (count >> 8) & 0xff;     /*  msb of counter value */
338         outb(byte, base_address + (counter_number << regshift));
339 }
340
341 static inline void i8254_mm_write(void *base_address,
342         unsigned int regshift, unsigned int counter_number, unsigned int count)
343 {
344         unsigned int byte;
345
346         if (counter_number > 2)
347                 return;
348
349         byte = count & 0xff;    /*  lsb of counter value */
350         writeb(byte, base_address + (counter_number << regshift));
351         byte = (count >> 8) & 0xff;     /*  msb of counter value */
352         writeb(byte, base_address + (counter_number << regshift));
353 }
354
355 /* Set counter mode, should work for 8253 also.
356  * Note: the 'mode' value is different to that for i8254_load() and comes
357  * from the INSN_CONFIG_8254_SET_MODE command:
358  *   I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
359  * OR'ed with:
360  *   I8254_BCD, I8254_BINARY
361  */
362 static inline int i8254_set_mode(unsigned long base_address,
363         unsigned int regshift, unsigned int counter_number, unsigned int mode)
364 {
365         unsigned int byte;
366
367         if (counter_number > 2)
368                 return -1;
369         if (mode > (I8254_MODE5 | I8254_BINARY))
370                 return -1;
371
372         byte = counter_number << 6;
373         byte |= 0x30;           /*  load low then high byte */
374         byte |= mode;           /*  set counter mode and BCD|binary */
375         outb(byte, base_address + (i8254_control_reg << regshift));
376
377         return 0;
378 }
379
380 static inline int i8254_mm_set_mode(void *base_address,
381         unsigned int regshift, unsigned int counter_number, unsigned int mode)
382 {
383         unsigned int byte;
384
385         if (counter_number > 2)
386                 return -1;
387         if (mode > (I8254_MODE5 | I8254_BINARY))
388                 return -1;
389
390         byte = counter_number << 6;
391         byte |= 0x30;           /*  load low then high byte */
392         byte |= mode;           /*  set counter mode and BCD|binary */
393         writeb(byte, base_address + (i8254_control_reg << regshift));
394
395         return 0;
396 }
397
398 static inline int i8254_status(unsigned long base_address,
399         unsigned int regshift, unsigned int counter_number)
400 {
401         outb(0xE0 | (2 << counter_number),
402                 base_address + (i8254_control_reg << regshift));
403         return inb(base_address + (counter_number << regshift));
404 }
405
406 static inline int i8254_mm_status(void *base_address,
407         unsigned int regshift, unsigned int counter_number)
408 {
409         writeb(0xE0 | (2 << counter_number),
410                 base_address + (i8254_control_reg << regshift));
411         return readb(base_address + (counter_number << regshift));
412 }
413
414 #endif
415
416 #endif