Merge tag 'spi-v4.0-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi
[linux-2.6-block.git] / drivers / staging / comedi / drivers / 8253.h
CommitLineData
703afc38 1/*
797f8894
IA
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 */
703afc38
DS
18
19#ifndef _8253_H
20#define _8253_H
21
703afc38 22#include "../comedi.h"
703afc38 23
cb9cfd7e
HS
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
d33f8858
HS
33static inline void i8253_cascade_ns_to_timer(int i8253_osc_base,
34 unsigned int *d1,
35 unsigned int *d2,
36 unsigned int *nanosec,
a207c12f 37 unsigned int flags)
703afc38
DS
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;
6400fb71
IA
47 /*
48 * exit early if everything is already correct (this can save time
703afc38 49 * since this function may be called repeatedly during command tests
6400fb71
IA
50 * and execution)
51 */
703afc38
DS
52 div1 = *d1 ? *d1 : max_count;
53 div2 = *d2 ? *d2 : max_count;
54 divider = div1 * div2;
55 if (div1 * div2 * i8253_osc_base == *nanosec &&
0a85b6f0
MT
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) {
703afc38
DS
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;
0a85b6f0 77 div1++) {
703afc38 78 for (div2 = divider / div1;
0a85b6f0
MT
79 div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
80 div2++) {
703afc38
DS
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
8a512e41
IA
95 switch (flags & CMDF_ROUND_MASK) {
96 case CMDF_ROUND_NEAREST:
703afc38
DS
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;
8a512e41 108 case CMDF_ROUND_UP:
703afc38
DS
109 div1 = div1_lub;
110 div2 = div2_lub;
111 break;
8a512e41 112 case CMDF_ROUND_DOWN:
703afc38
DS
113 div1 = div1_glb;
114 div2 = div2_glb;
115 break;
116 }
117
118 *nanosec = div1 * div2 * i8253_osc_base;
5cc1e33c 119 /* masking is done since counter maps zero to 0x10000 */
155b44aa 120 *d1 = div1 & 0xffff;
703afc38 121 *d2 = div2 & 0xffff;
703afc38
DS
122}
123
124#ifndef CMDTEST
6400fb71
IA
125/*
126 * i8254_load programs 8254 counter chip. It should also work for the 8253.
a88e29cf
GH
127 * base_address is the lowest io address
128 * for the chip (the address of counter 0).
703afc38
DS
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
148static inline int i8254_load(unsigned long base_address, unsigned int regshift,
0a85b6f0
MT
149 unsigned int counter_number, unsigned int count,
150 unsigned int mode)
703afc38
DS
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;
5cc1e33c
IA
164 byte |= 0x30; /* load low then high byte */
165 byte |= (mode << 1); /* set counter mode */
703afc38 166 outb(byte, base_address + (i8254_control_reg << regshift));
5cc1e33c 167 byte = count & 0xff; /* lsb of counter value */
703afc38 168 outb(byte, base_address + (counter_number << regshift));
5cc1e33c 169 byte = (count >> 8) & 0xff; /* msb of counter value */
703afc38
DS
170 outb(byte, base_address + (counter_number << regshift));
171
172 return 0;
173}
174
5743aaac
HS
175static inline int i8254_mm_load(void __iomem *base_address,
176 unsigned int regshift,
177 unsigned int counter_number,
178 unsigned int count,
0a85b6f0 179 unsigned int mode)
703afc38
DS
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;
5cc1e33c
IA
193 byte |= 0x30; /* load low then high byte */
194 byte |= (mode << 1); /* set counter mode */
703afc38 195 writeb(byte, base_address + (i8254_control_reg << regshift));
5cc1e33c 196 byte = count & 0xff; /* lsb of counter value */
703afc38 197 writeb(byte, base_address + (counter_number << regshift));
5cc1e33c 198 byte = (count >> 8) & 0xff; /* msb of counter value */
703afc38
DS
199 writeb(byte, base_address + (counter_number << regshift));
200
201 return 0;
202}
203
5cc1e33c 204/* Returns 16 bit counter value, should work for 8253 also. */
703afc38 205static inline int i8254_read(unsigned long base_address, unsigned int regshift,
0a85b6f0 206 unsigned int counter_number)
703afc38
DS
207{
208 unsigned int byte;
209 int ret;
210
211 if (counter_number > 2)
212 return -1;
213
5cc1e33c 214 /* latch counter */
703afc38
DS
215 byte = counter_number << 6;
216 outb(byte, base_address + (i8254_control_reg << regshift));
217
5cc1e33c 218 /* read lsb */
703afc38 219 ret = inb(base_address + (counter_number << regshift));
5cc1e33c 220 /* read msb */
703afc38
DS
221 ret += inb(base_address + (counter_number << regshift)) << 8;
222
223 return ret;
224}
225
5743aaac
HS
226static inline int i8254_mm_read(void __iomem *base_address,
227 unsigned int regshift,
0a85b6f0 228 unsigned int counter_number)
703afc38
DS
229{
230 unsigned int byte;
231 int ret;
232
233 if (counter_number > 2)
234 return -1;
235
5cc1e33c 236 /* latch counter */
703afc38
DS
237 byte = counter_number << 6;
238 writeb(byte, base_address + (i8254_control_reg << regshift));
239
5cc1e33c 240 /* read lsb */
703afc38 241 ret = readb(base_address + (counter_number << regshift));
5cc1e33c 242 /* read msb */
703afc38
DS
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. */
249static inline void i8254_write(unsigned long base_address,
0a85b6f0
MT
250 unsigned int regshift,
251 unsigned int counter_number, unsigned int count)
703afc38
DS
252{
253 unsigned int byte;
254
255 if (counter_number > 2)
256 return;
257
5cc1e33c 258 byte = count & 0xff; /* lsb of counter value */
703afc38 259 outb(byte, base_address + (counter_number << regshift));
5cc1e33c 260 byte = (count >> 8) & 0xff; /* msb of counter value */
703afc38
DS
261 outb(byte, base_address + (counter_number << regshift));
262}
263
5743aaac 264static inline void i8254_mm_write(void __iomem *base_address,
0a85b6f0
MT
265 unsigned int regshift,
266 unsigned int counter_number,
267 unsigned int count)
703afc38
DS
268{
269 unsigned int byte;
270
271 if (counter_number > 2)
272 return;
273
5cc1e33c 274 byte = count & 0xff; /* lsb of counter value */
703afc38 275 writeb(byte, base_address + (counter_number << regshift));
5cc1e33c 276 byte = (count >> 8) & 0xff; /* msb of counter value */
703afc38
DS
277 writeb(byte, base_address + (counter_number << regshift));
278}
279
6400fb71
IA
280/*
281 * Set counter mode, should work for 8253 also.
703afc38
DS
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 */
288static inline int i8254_set_mode(unsigned long base_address,
0a85b6f0
MT
289 unsigned int regshift,
290 unsigned int counter_number, unsigned int mode)
703afc38
DS
291{
292 unsigned int byte;
293
294 if (counter_number > 2)
295 return -1;
7d52477e 296 if (mode > (I8254_MODE5 | I8254_BCD))
703afc38
DS
297 return -1;
298
299 byte = counter_number << 6;
5cc1e33c
IA
300 byte |= 0x30; /* load low then high byte */
301 byte |= mode; /* set counter mode and BCD|binary */
703afc38
DS
302 outb(byte, base_address + (i8254_control_reg << regshift));
303
304 return 0;
305}
306
5743aaac 307static inline int i8254_mm_set_mode(void __iomem *base_address,
0a85b6f0
MT
308 unsigned int regshift,
309 unsigned int counter_number,
310 unsigned int mode)
703afc38
DS
311{
312 unsigned int byte;
313
314 if (counter_number > 2)
315 return -1;
7d52477e 316 if (mode > (I8254_MODE5 | I8254_BCD))
703afc38
DS
317 return -1;
318
319 byte = counter_number << 6;
5cc1e33c
IA
320 byte |= 0x30; /* load low then high byte */
321 byte |= mode; /* set counter mode and BCD|binary */
703afc38
DS
322 writeb(byte, base_address + (i8254_control_reg << regshift));
323
324 return 0;
325}
326
327static inline int i8254_status(unsigned long base_address,
0a85b6f0
MT
328 unsigned int regshift,
329 unsigned int counter_number)
703afc38
DS
330{
331 outb(0xE0 | (2 << counter_number),
0a85b6f0 332 base_address + (i8254_control_reg << regshift));
703afc38
DS
333 return inb(base_address + (counter_number << regshift));
334}
335
5743aaac 336static inline int i8254_mm_status(void __iomem *base_address,
0a85b6f0
MT
337 unsigned int regshift,
338 unsigned int counter_number)
703afc38
DS
339{
340 writeb(0xE0 | (2 << counter_number),
0a85b6f0 341 base_address + (i8254_control_reg << regshift));
703afc38
DS
342 return readb(base_address + (counter_number << regshift));
343}
344
345#endif
346
347#endif