1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116#include <linux/module.h>
117#include <linux/slab.h>
118#include <linux/io.h>
119
120#include "../comedidev.h"
121
122#include "comedi_8254.h"
123
124static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
125{
126 unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
127 unsigned int val;
128
129 switch (i8254->iosize) {
130 default:
131 case I8254_IO8:
132 if (i8254->mmio)
133 val = readb(i8254->mmio + reg_offset);
134 else
135 val = inb(i8254->iobase + reg_offset);
136 break;
137 case I8254_IO16:
138 if (i8254->mmio)
139 val = readw(i8254->mmio + reg_offset);
140 else
141 val = inw(i8254->iobase + reg_offset);
142 break;
143 case I8254_IO32:
144 if (i8254->mmio)
145 val = readl(i8254->mmio + reg_offset);
146 else
147 val = inl(i8254->iobase + reg_offset);
148 break;
149 }
150 return val & 0xff;
151}
152
153static void __i8254_write(struct comedi_8254 *i8254,
154 unsigned int val, unsigned int reg)
155{
156 unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
157
158 switch (i8254->iosize) {
159 default:
160 case I8254_IO8:
161 if (i8254->mmio)
162 writeb(val, i8254->mmio + reg_offset);
163 else
164 outb(val, i8254->iobase + reg_offset);
165 break;
166 case I8254_IO16:
167 if (i8254->mmio)
168 writew(val, i8254->mmio + reg_offset);
169 else
170 outw(val, i8254->iobase + reg_offset);
171 break;
172 case I8254_IO32:
173 if (i8254->mmio)
174 writel(val, i8254->mmio + reg_offset);
175 else
176 outl(val, i8254->iobase + reg_offset);
177 break;
178 }
179}
180
181
182
183
184
185
186unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
187{
188 unsigned int cmd;
189
190 if (counter > 2)
191 return 0;
192
193 cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
194 __i8254_write(i8254, cmd, I8254_CTRL_REG);
195
196 return __i8254_read(i8254, counter);
197}
198EXPORT_SYMBOL_GPL(comedi_8254_status);
199
200
201
202
203
204
205unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
206{
207 unsigned int val;
208
209 if (counter > 2)
210 return 0;
211
212
213 __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
214 I8254_CTRL_REG);
215
216
217 val = __i8254_read(i8254, counter);
218 val |= (__i8254_read(i8254, counter) << 8);
219
220 return val;
221}
222EXPORT_SYMBOL_GPL(comedi_8254_read);
223
224
225
226
227
228
229
230void comedi_8254_write(struct comedi_8254 *i8254,
231 unsigned int counter, unsigned int val)
232{
233 unsigned int byte;
234
235 if (counter > 2)
236 return;
237 if (val > 0xffff)
238 return;
239
240
241 byte = val & 0xff;
242 __i8254_write(i8254, byte, counter);
243 byte = (val >> 8) & 0xff;
244 __i8254_write(i8254, byte, counter);
245}
246EXPORT_SYMBOL_GPL(comedi_8254_write);
247
248
249
250
251
252
253
254int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
255 unsigned int mode)
256{
257 unsigned int byte;
258
259 if (counter > 2)
260 return -EINVAL;
261 if (mode > (I8254_MODE5 | I8254_BCD))
262 return -EINVAL;
263
264 byte = I8254_CTRL_SEL_CTR(counter) |
265 I8254_CTRL_LSB_MSB |
266 mode;
267 __i8254_write(i8254, byte, I8254_CTRL_REG);
268
269 return 0;
270}
271EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
272
273
274
275
276
277
278
279
280int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
281 unsigned int val, unsigned int mode)
282{
283 if (counter > 2)
284 return -EINVAL;
285 if (val > 0xffff)
286 return -EINVAL;
287 if (mode > (I8254_MODE5 | I8254_BCD))
288 return -EINVAL;
289
290 comedi_8254_set_mode(i8254, counter, mode);
291 comedi_8254_write(i8254, counter, val);
292
293 return 0;
294}
295EXPORT_SYMBOL_GPL(comedi_8254_load);
296
297
298
299
300
301
302
303
304void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
305 unsigned int counter1,
306 unsigned int counter2,
307 bool enable)
308{
309 unsigned int mode;
310
311 if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
312 return;
313
314 if (enable)
315 mode = I8254_MODE2 | I8254_BINARY;
316 else
317 mode = I8254_MODE0 | I8254_BINARY;
318
319 comedi_8254_set_mode(i8254, counter1, mode);
320 comedi_8254_set_mode(i8254, counter2, mode);
321
322 if (enable) {
323
324
325
326
327
328 comedi_8254_write(i8254, counter2, i8254->divisor2);
329 comedi_8254_write(i8254, counter1, i8254->divisor1);
330 }
331}
332EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
333
334
335
336
337
338void comedi_8254_update_divisors(struct comedi_8254 *i8254)
339{
340
341 i8254->divisor = i8254->next_div & 0xffff;
342 i8254->divisor1 = i8254->next_div1 & 0xffff;
343 i8254->divisor2 = i8254->next_div2 & 0xffff;
344}
345EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
346
347
348
349
350
351
352
353void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
354 unsigned int *nanosec,
355 unsigned int flags)
356{
357 unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
358 unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
359 unsigned int div = d1 * d2;
360 unsigned int ns_lub = 0xffffffff;
361 unsigned int ns_glb = 0;
362 unsigned int d1_lub = 0;
363 unsigned int d1_glb = 0;
364 unsigned int d2_lub = 0;
365 unsigned int d2_glb = 0;
366 unsigned int start;
367 unsigned int ns;
368 unsigned int ns_low;
369 unsigned int ns_high;
370
371
372 if (div * i8254->osc_base == *nanosec &&
373 d1 > 1 && d1 <= I8254_MAX_COUNT &&
374 d2 > 1 && d2 <= I8254_MAX_COUNT &&
375
376 div > d1 && div > d2 &&
377 div * i8254->osc_base > div &&
378 div * i8254->osc_base > i8254->osc_base)
379 return;
380
381 div = *nanosec / i8254->osc_base;
382 d2 = I8254_MAX_COUNT;
383 start = div / d2;
384 if (start < 2)
385 start = 2;
386 for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
387 for (d2 = div / d1;
388 d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
389 ns = i8254->osc_base * d1 * d2;
390 if (ns <= *nanosec && ns > ns_glb) {
391 ns_glb = ns;
392 d1_glb = d1;
393 d2_glb = d2;
394 }
395 if (ns >= *nanosec && ns < ns_lub) {
396 ns_lub = ns;
397 d1_lub = d1;
398 d2_lub = d2;
399 }
400 }
401 }
402
403 switch (flags & CMDF_ROUND_MASK) {
404 case CMDF_ROUND_NEAREST:
405 default:
406 ns_high = d1_lub * d2_lub * i8254->osc_base;
407 ns_low = d1_glb * d2_glb * i8254->osc_base;
408 if (ns_high - *nanosec < *nanosec - ns_low) {
409 d1 = d1_lub;
410 d2 = d2_lub;
411 } else {
412 d1 = d1_glb;
413 d2 = d2_glb;
414 }
415 break;
416 case CMDF_ROUND_UP:
417 d1 = d1_lub;
418 d2 = d2_lub;
419 break;
420 case CMDF_ROUND_DOWN:
421 d1 = d1_glb;
422 d2 = d2_glb;
423 break;
424 }
425
426 *nanosec = d1 * d2 * i8254->osc_base;
427 i8254->next_div1 = d1;
428 i8254->next_div2 = d2;
429}
430EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
431
432
433
434
435
436
437
438void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
439 unsigned int *nanosec, unsigned int flags)
440{
441 unsigned int divisor;
442
443 switch (flags & CMDF_ROUND_MASK) {
444 default:
445 case CMDF_ROUND_NEAREST:
446 divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
447 break;
448 case CMDF_ROUND_UP:
449 divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
450 break;
451 case CMDF_ROUND_DOWN:
452 divisor = *nanosec / i8254->osc_base;
453 break;
454 }
455 if (divisor < 2)
456 divisor = 2;
457 if (divisor > I8254_MAX_COUNT)
458 divisor = I8254_MAX_COUNT;
459
460 *nanosec = divisor * i8254->osc_base;
461 i8254->next_div = divisor;
462}
463EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
464
465
466
467
468
469
470
471void comedi_8254_set_busy(struct comedi_8254 *i8254,
472 unsigned int counter, bool busy)
473{
474 if (counter < 3)
475 i8254->busy[counter] = busy;
476}
477EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
478
479static int comedi_8254_insn_read(struct comedi_device *dev,
480 struct comedi_subdevice *s,
481 struct comedi_insn *insn,
482 unsigned int *data)
483{
484 struct comedi_8254 *i8254 = s->private;
485 unsigned int chan = CR_CHAN(insn->chanspec);
486 int i;
487
488 if (i8254->busy[chan])
489 return -EBUSY;
490
491 for (i = 0; i < insn->n; i++)
492 data[i] = comedi_8254_read(i8254, chan);
493
494 return insn->n;
495}
496
497static int comedi_8254_insn_write(struct comedi_device *dev,
498 struct comedi_subdevice *s,
499 struct comedi_insn *insn,
500 unsigned int *data)
501{
502 struct comedi_8254 *i8254 = s->private;
503 unsigned int chan = CR_CHAN(insn->chanspec);
504
505 if (i8254->busy[chan])
506 return -EBUSY;
507
508 if (insn->n)
509 comedi_8254_write(i8254, chan, data[insn->n - 1]);
510
511 return insn->n;
512}
513
514static int comedi_8254_insn_config(struct comedi_device *dev,
515 struct comedi_subdevice *s,
516 struct comedi_insn *insn,
517 unsigned int *data)
518{
519 struct comedi_8254 *i8254 = s->private;
520 unsigned int chan = CR_CHAN(insn->chanspec);
521 int ret;
522
523 if (i8254->busy[chan])
524 return -EBUSY;
525
526 switch (data[0]) {
527 case INSN_CONFIG_RESET:
528 ret = comedi_8254_set_mode(i8254, chan,
529 I8254_MODE0 | I8254_BINARY);
530 if (ret)
531 return ret;
532 break;
533 case INSN_CONFIG_SET_COUNTER_MODE:
534 ret = comedi_8254_set_mode(i8254, chan, data[1]);
535 if (ret)
536 return ret;
537 break;
538 case INSN_CONFIG_8254_READ_STATUS:
539 data[1] = comedi_8254_status(i8254, chan);
540 break;
541 default:
542
543
544
545
546 if (i8254->insn_config)
547 return i8254->insn_config(dev, s, insn, data);
548
549 return -EINVAL;
550 }
551
552 return insn->n;
553}
554
555
556
557
558
559void comedi_8254_subdevice_init(struct comedi_subdevice *s,
560 struct comedi_8254 *i8254)
561{
562 s->type = COMEDI_SUBD_COUNTER;
563 s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
564 s->n_chan = 3;
565 s->maxdata = 0xffff;
566 s->range_table = &range_unknown;
567 s->insn_read = comedi_8254_insn_read;
568 s->insn_write = comedi_8254_insn_write;
569 s->insn_config = comedi_8254_insn_config;
570
571 s->private = i8254;
572}
573EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
574
575static struct comedi_8254 *__i8254_init(unsigned long iobase,
576 void __iomem *mmio,
577 unsigned int osc_base,
578 unsigned int iosize,
579 unsigned int regshift)
580{
581 struct comedi_8254 *i8254;
582 int i;
583
584
585 if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
586 iosize == I8254_IO32))
587 return NULL;
588
589 i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
590 if (!i8254)
591 return NULL;
592
593 i8254->iobase = iobase;
594 i8254->mmio = mmio;
595 i8254->iosize = iosize;
596 i8254->regshift = regshift;
597
598
599 i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
600
601
602 for (i = 0; i < 3; i++)
603 comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
604
605 return i8254;
606}
607
608
609
610
611
612
613
614
615
616struct comedi_8254 *comedi_8254_init(unsigned long iobase,
617 unsigned int osc_base,
618 unsigned int iosize,
619 unsigned int regshift)
620{
621 return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
622}
623EXPORT_SYMBOL_GPL(comedi_8254_init);
624
625
626
627
628
629
630
631
632
633struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
634 unsigned int osc_base,
635 unsigned int iosize,
636 unsigned int regshift)
637{
638 return __i8254_init(0, mmio, osc_base, iosize, regshift);
639}
640EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
641
642static int __init comedi_8254_module_init(void)
643{
644 return 0;
645}
646module_init(comedi_8254_module_init);
647
648static void __exit comedi_8254_module_exit(void)
649{
650}
651module_exit(comedi_8254_module_exit);
652
653MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
654MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
655MODULE_LICENSE("GPL");
656