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