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