]>
git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - drivers/staging/comedi/drivers/8253.h
2 * comedi/drivers/8253.h
5 * COMEDI - Linux Control and Measurement Device Interface
6 * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
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.
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.
22 #include "../comedi.h"
25 * Common oscillator base values in nanoseconds
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
33 static inline void i8253_cascade_ns_to_timer(int i8253_osc_base
,
36 unsigned int *nanosec
,
40 unsigned int div1
, div2
;
41 unsigned int div1_glb
, div2_glb
, ns_glb
;
42 unsigned int div1_lub
, div2_lub
, ns_lub
;
45 unsigned int ns_low
, ns_high
;
46 static const unsigned int max_count
= 0x10000;
48 * exit early if everything is already correct (this can save time
49 * since this function may be called repeatedly during command tests
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
) {
64 divider
= *nanosec
/ i8253_osc_base
;
66 div1_lub
= div2_lub
= 0;
67 div1_glb
= div2_glb
= 0;
73 start
= divider
/ div2
;
76 for (div1
= start
; div1
<= divider
/ div1
+ 1 && div1
<= max_count
;
78 for (div2
= divider
/ div1
;
79 div1
* div2
<= divider
+ div1
+ 1 && div2
<= max_count
;
81 ns
= i8253_osc_base
* div1
* div2
;
82 if (ns
<= *nanosec
&& ns
> ns_glb
) {
87 if (ns
>= *nanosec
&& ns
< ns_lub
) {
95 switch (flags
& CMDF_ROUND_MASK
) {
96 case CMDF_ROUND_NEAREST
:
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
) {
112 case CMDF_ROUND_DOWN
:
118 *nanosec
= div1
* div2
* i8253_osc_base
;
119 /* masking is done since counter maps zero to 0x10000 */
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.
132 * You probably want to use mode 2.
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().
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
146 #define i8254_control_reg 3
148 static inline int i8254_load(unsigned long base_address
, unsigned int regshift
,
149 unsigned int counter_number
, unsigned int count
,
154 if (counter_number
> 2)
160 if ((mode
== 2 || mode
== 3) && count
== 1)
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
));
175 static inline int i8254_mm_load(void __iomem
*base_address
,
176 unsigned int regshift
,
177 unsigned int counter_number
,
183 if (counter_number
> 2)
189 if ((mode
== 2 || mode
== 3) && count
== 1)
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
));
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
)
211 if (counter_number
> 2)
215 byte
= counter_number
<< 6;
216 outb(byte
, base_address
+ (i8254_control_reg
<< regshift
));
219 ret
= inb(base_address
+ (counter_number
<< regshift
));
221 ret
+= inb(base_address
+ (counter_number
<< regshift
)) << 8;
226 static inline int i8254_mm_read(void __iomem
*base_address
,
227 unsigned int regshift
,
228 unsigned int counter_number
)
233 if (counter_number
> 2)
237 byte
= counter_number
<< 6;
238 writeb(byte
, base_address
+ (i8254_control_reg
<< regshift
));
241 ret
= readb(base_address
+ (counter_number
<< regshift
));
243 ret
+= readb(base_address
+ (counter_number
<< regshift
)) << 8;
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
)
255 if (counter_number
> 2)
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
));
264 static inline void i8254_mm_write(void __iomem
*base_address
,
265 unsigned int regshift
,
266 unsigned int counter_number
,
271 if (counter_number
> 2)
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
));
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
286 * I8254_BCD, I8254_BINARY
288 static inline int i8254_set_mode(unsigned long base_address
,
289 unsigned int regshift
,
290 unsigned int counter_number
, unsigned int mode
)
294 if (counter_number
> 2)
296 if (mode
> (I8254_MODE5
| I8254_BCD
))
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
));
307 static inline int i8254_mm_set_mode(void __iomem
*base_address
,
308 unsigned int regshift
,
309 unsigned int counter_number
,
314 if (counter_number
> 2)
316 if (mode
> (I8254_MODE5
| I8254_BCD
))
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
));
327 static inline int i8254_status(unsigned long base_address
,
328 unsigned int regshift
,
329 unsigned int counter_number
)
331 outb(0xE0 | (2 << counter_number
),
332 base_address
+ (i8254_control_reg
<< regshift
));
333 return inb(base_address
+ (counter_number
<< regshift
));
336 static inline int i8254_mm_status(void __iomem
*base_address
,
337 unsigned int regshift
,
338 unsigned int counter_number
)
340 writeb(0xE0 | (2 << counter_number
),
341 base_address
+ (i8254_control_reg
<< regshift
));
342 return readb(base_address
+ (counter_number
<< regshift
));