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