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