Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
8253.h
Go to the documentation of this file.
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 <[email protected]>
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  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 
22 */
23 
24 #ifndef _8253_H
25 #define _8253_H
26 
27 #include "../comedi.h"
28 
29 #define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
30 
31 static inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
32  unsigned int *d1,
33  unsigned int *d2,
34  unsigned int *nanosec,
35  int round_mode)
36 {
37  int divider;
38  int div1, div2;
39  int div1_glb, div2_glb, ns_glb;
40  int div1_lub, div2_lub, ns_lub;
41  int ns;
42 
43  divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
44 
45  /* find 2 integers 1<={x,y}<=65536 such that x*y is
46  close to divider */
47 
48  div1_lub = div2_lub = 0;
49  div1_glb = div2_glb = 0;
50 
51  ns_glb = 0;
52  ns_lub = 0xffffffff;
53 
54  div2 = 0x10000;
55  for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
56  div2 = divider / div1;
57 
58  ns = i8253_osc_base * div1 * div2;
59  if (ns <= *nanosec && ns > ns_glb) {
60  ns_glb = ns;
61  div1_glb = div1;
62  div2_glb = div2;
63  }
64 
65  div2++;
66  if (div2 <= 65536) {
67  ns = i8253_osc_base * div1 * div2;
68  if (ns > *nanosec && ns < ns_lub) {
69  ns_lub = ns;
70  div1_lub = div1;
71  div2_lub = div2;
72  }
73  }
74  }
75 
76  *nanosec = div1_lub * div2_lub * i8253_osc_base;
77  *d1 = div1_lub & 0xffff;
78  *d2 = div2_lub & 0xffff;
79  return;
80 }
81 
82 static inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
83  unsigned int *d1,
84  unsigned int *d2,
85  unsigned int *nanosec,
86  int round_mode)
87 {
88  int div1, div2;
89  int base;
90 
91  for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
92  base = i8253_osc_base * div1;
93  round_mode &= TRIG_ROUND_MASK;
94  switch (round_mode) {
95  case TRIG_ROUND_NEAREST:
96  default:
97  div2 = (*nanosec + base / 2) / base;
98  break;
99  case TRIG_ROUND_DOWN:
100  div2 = (*nanosec) / base;
101  break;
102  case TRIG_ROUND_UP:
103  div2 = (*nanosec + base - 1) / base;
104  break;
105  }
106  if (div2 < 2)
107  div2 = 2;
108  if (div2 <= 65536) {
109  *nanosec = div2 * base;
110  *d1 = div1 & 0xffff;
111  *d2 = div2 & 0xffff;
112  return;
113  }
114  }
115 
116  /* shouldn't get here */
117  div1 = 0x10000;
118  div2 = 0x10000;
119  *nanosec = div1 * div2 * i8253_osc_base;
120  *d1 = div1 & 0xffff;
121  *d2 = div2 & 0xffff;
122 }
123 
124 static inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
125  unsigned int *d1,
126  unsigned int *d2,
127  unsigned int *nanosec,
128  int round_mode)
129 {
130  unsigned int divider;
131  unsigned int div1, div2;
132  unsigned int div1_glb, div2_glb, ns_glb;
133  unsigned int div1_lub, div2_lub, ns_lub;
134  unsigned int ns;
135  unsigned int start;
136  unsigned int ns_low, ns_high;
137  static const unsigned int max_count = 0x10000;
138  /* exit early if everything is already correct (this can save time
139  * since this function may be called repeatedly during command tests
140  * and execution) */
141  div1 = *d1 ? *d1 : max_count;
142  div2 = *d2 ? *d2 : max_count;
143  divider = div1 * div2;
144  if (div1 * div2 * i8253_osc_base == *nanosec &&
145  div1 > 1 && div1 <= max_count && div2 > 1 && div2 <= max_count &&
146  /* check for overflow */
147  divider > div1 && divider > div2 &&
148  divider * i8253_osc_base > divider &&
149  divider * i8253_osc_base > i8253_osc_base) {
150  return;
151  }
152 
153  divider = *nanosec / i8253_osc_base;
154 
155  div1_lub = div2_lub = 0;
156  div1_glb = div2_glb = 0;
157 
158  ns_glb = 0;
159  ns_lub = 0xffffffff;
160 
161  div2 = max_count;
162  start = divider / div2;
163  if (start < 2)
164  start = 2;
165  for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
166  div1++) {
167  for (div2 = divider / div1;
168  div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
169  div2++) {
170  ns = i8253_osc_base * div1 * div2;
171  if (ns <= *nanosec && ns > ns_glb) {
172  ns_glb = ns;
173  div1_glb = div1;
174  div2_glb = div2;
175  }
176  if (ns >= *nanosec && ns < ns_lub) {
177  ns_lub = ns;
178  div1_lub = div1;
179  div2_lub = div2;
180  }
181  }
182  }
183 
184  round_mode &= TRIG_ROUND_MASK;
185  switch (round_mode) {
186  case TRIG_ROUND_NEAREST:
187  default:
188  ns_high = div1_lub * div2_lub * i8253_osc_base;
189  ns_low = div1_glb * div2_glb * i8253_osc_base;
190  if (ns_high - *nanosec < *nanosec - ns_low) {
191  div1 = div1_lub;
192  div2 = div2_lub;
193  } else {
194  div1 = div1_glb;
195  div2 = div2_glb;
196  }
197  break;
198  case TRIG_ROUND_UP:
199  div1 = div1_lub;
200  div2 = div2_lub;
201  break;
202  case TRIG_ROUND_DOWN:
203  div1 = div1_glb;
204  div2 = div2_glb;
205  break;
206  }
207 
208  *nanosec = div1 * div2 * i8253_osc_base;
209  /* masking is done since counter maps zero to 0x10000 */
210  *d1 = div1 & 0xffff;
211  *d2 = div2 & 0xffff;
212  return;
213 }
214 
215 #ifndef CMDTEST
216 /* i8254_load programs 8254 counter chip. It should also work for the 8253.
217  * base_address is the lowest io address
218  * for the chip (the address of counter 0).
219  * counter_number is the counter you want to load (0,1 or 2)
220  * count is the number to load into the counter.
221  *
222  * You probably want to use mode 2.
223  *
224  * Use i8254_mm_load() if you board uses memory-mapped io, it is
225  * the same as i8254_load() except it uses writeb() instead of outb().
226  *
227  * Neither i8254_load() or i8254_read() do their loading/reading
228  * atomically. The 16 bit read/writes are performed with two successive
229  * 8 bit read/writes. So if two parts of your driver do a load/read on
230  * the same counter, it may be necessary to protect these functions
231  * with a spinlock.
232  *
233  * FMH
234  */
235 
236 #define i8254_control_reg 3
237 
238 static inline int i8254_load(unsigned long base_address, unsigned int regshift,
239  unsigned int counter_number, unsigned int count,
240  unsigned int mode)
241 {
242  unsigned int byte;
243 
244  if (counter_number > 2)
245  return -1;
246  if (count > 0xffff)
247  return -1;
248  if (mode > 5)
249  return -1;
250  if ((mode == 2 || mode == 3) && count == 1)
251  return -1;
252 
253  byte = counter_number << 6;
254  byte |= 0x30; /* load low then high byte */
255  byte |= (mode << 1); /* set counter mode */
256  outb(byte, base_address + (i8254_control_reg << regshift));
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  return 0;
263 }
264 
265 static inline int i8254_mm_load(void __iomem *base_address,
266  unsigned int regshift,
267  unsigned int counter_number,
268  unsigned int count,
269  unsigned int mode)
270 {
271  unsigned int byte;
272 
273  if (counter_number > 2)
274  return -1;
275  if (count > 0xffff)
276  return -1;
277  if (mode > 5)
278  return -1;
279  if ((mode == 2 || mode == 3) && count == 1)
280  return -1;
281 
282  byte = counter_number << 6;
283  byte |= 0x30; /* load low then high byte */
284  byte |= (mode << 1); /* set counter mode */
285  writeb(byte, base_address + (i8254_control_reg << regshift));
286  byte = count & 0xff; /* lsb of counter value */
287  writeb(byte, base_address + (counter_number << regshift));
288  byte = (count >> 8) & 0xff; /* msb of counter value */
289  writeb(byte, base_address + (counter_number << regshift));
290 
291  return 0;
292 }
293 
294 /* Returns 16 bit counter value, should work for 8253 also.*/
295 static inline int i8254_read(unsigned long base_address, unsigned int regshift,
296  unsigned int counter_number)
297 {
298  unsigned int byte;
299  int ret;
300 
301  if (counter_number > 2)
302  return -1;
303 
304  /* latch counter */
305  byte = counter_number << 6;
306  outb(byte, base_address + (i8254_control_reg << regshift));
307 
308  /* read lsb */
309  ret = inb(base_address + (counter_number << regshift));
310  /* read msb */
311  ret += inb(base_address + (counter_number << regshift)) << 8;
312 
313  return ret;
314 }
315 
316 static inline int i8254_mm_read(void __iomem *base_address,
317  unsigned int regshift,
318  unsigned int counter_number)
319 {
320  unsigned int byte;
321  int ret;
322 
323  if (counter_number > 2)
324  return -1;
325 
326  /* latch counter */
327  byte = counter_number << 6;
328  writeb(byte, base_address + (i8254_control_reg << regshift));
329 
330  /* read lsb */
331  ret = readb(base_address + (counter_number << regshift));
332  /* read msb */
333  ret += readb(base_address + (counter_number << regshift)) << 8;
334 
335  return ret;
336 }
337 
338 /* Loads 16 bit initial counter value, should work for 8253 also. */
339 static inline void i8254_write(unsigned long base_address,
340  unsigned int regshift,
341  unsigned int counter_number, unsigned int count)
342 {
343  unsigned int byte;
344 
345  if (counter_number > 2)
346  return;
347 
348  byte = count & 0xff; /* lsb of counter value */
349  outb(byte, base_address + (counter_number << regshift));
350  byte = (count >> 8) & 0xff; /* msb of counter value */
351  outb(byte, base_address + (counter_number << regshift));
352 }
353 
354 static inline void i8254_mm_write(void __iomem *base_address,
355  unsigned int regshift,
356  unsigned int counter_number,
357  unsigned int count)
358 {
359  unsigned int byte;
360 
361  if (counter_number > 2)
362  return;
363 
364  byte = count & 0xff; /* lsb of counter value */
365  writeb(byte, base_address + (counter_number << regshift));
366  byte = (count >> 8) & 0xff; /* msb of counter value */
367  writeb(byte, base_address + (counter_number << regshift));
368 }
369 
370 /* Set counter mode, should work for 8253 also.
371  * Note: the 'mode' value is different to that for i8254_load() and comes
372  * from the INSN_CONFIG_8254_SET_MODE command:
373  * I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
374  * OR'ed with:
375  * I8254_BCD, I8254_BINARY
376  */
377 static inline int i8254_set_mode(unsigned long base_address,
378  unsigned int regshift,
379  unsigned int counter_number, unsigned int mode)
380 {
381  unsigned int byte;
382 
383  if (counter_number > 2)
384  return -1;
385  if (mode > (I8254_MODE5 | I8254_BINARY))
386  return -1;
387 
388  byte = counter_number << 6;
389  byte |= 0x30; /* load low then high byte */
390  byte |= mode; /* set counter mode and BCD|binary */
391  outb(byte, base_address + (i8254_control_reg << regshift));
392 
393  return 0;
394 }
395 
396 static inline int i8254_mm_set_mode(void __iomem *base_address,
397  unsigned int regshift,
398  unsigned int counter_number,
399  unsigned int mode)
400 {
401  unsigned int byte;
402 
403  if (counter_number > 2)
404  return -1;
405  if (mode > (I8254_MODE5 | I8254_BINARY))
406  return -1;
407 
408  byte = counter_number << 6;
409  byte |= 0x30; /* load low then high byte */
410  byte |= mode; /* set counter mode and BCD|binary */
411  writeb(byte, base_address + (i8254_control_reg << regshift));
412 
413  return 0;
414 }
415 
416 static inline int i8254_status(unsigned long base_address,
417  unsigned int regshift,
418  unsigned int counter_number)
419 {
420  outb(0xE0 | (2 << counter_number),
421  base_address + (i8254_control_reg << regshift));
422  return inb(base_address + (counter_number << regshift));
423 }
424 
425 static inline int i8254_mm_status(void __iomem *base_address,
426  unsigned int regshift,
427  unsigned int counter_number)
428 {
429  writeb(0xE0 | (2 << counter_number),
430  base_address + (i8254_control_reg << regshift));
431  return readb(base_address + (counter_number << regshift));
432 }
433 
434 #endif
435 
436 #endif