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