GNU Linux-libre 4.19.245-gnu1
[releases.git] / drivers / staging / comedi / drivers / comedi_8254.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * comedi_8254.c
4  * Generic 8254 timer/counter support
5  * Copyright (C) 2014 H Hartley Sweeten <hsweeten@visionengravers.com>
6  *
7  * Based on 8253.h and various subdevice implementations in comedi drivers.
8  *
9  * COMEDI - Linux Control and Measurement Device Interface
10  * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
11  */
12
13 /*
14  * Module: comedi_8254
15  * Description: Generic 8254 timer/counter support
16  * Author: H Hartley Sweeten <hsweeten@visionengravers.com>
17  * Updated: Thu Jan 8 16:45:45 MST 2015
18  * Status: works
19  *
20  * This module is not used directly by end-users. Rather, it is used by other
21  * drivers to provide support for an 8254 Programmable Interval Timer. These
22  * counters are typically used to generate the pacer clock used for data
23  * acquisition. Some drivers also expose the counters for general purpose use.
24  *
25  * This module provides the following basic functions:
26  *
27  * comedi_8254_init() / comedi_8254_mm_init()
28  *      Initializes this module to access the 8254 registers. The _mm version
29  *      sets up the module for MMIO register access the other for PIO access.
30  *      The pointer returned from these functions is normally stored in the
31  *      comedi_device dev->pacer and will be freed by the comedi core during
32  *      the driver (*detach). If a driver has multiple 8254 devices, they need
33  *      to be stored in the drivers private data and freed when the driver is
34  *      detached.
35  *
36  *      NOTE: The counters are reset by setting them to I8254_MODE0 as part of
37  *      this initialization.
38  *
39  * comedi_8254_set_mode()
40  *      Sets a counters operation mode:
41  *              I8254_MODE0     Interrupt on terminal count
42  *              I8254_MODE1     Hardware retriggerable one-shot
43  *              I8254_MODE2     Rate generator
44  *              I8254_MODE3     Square wave mode
45  *              I8254_MODE4     Software triggered strobe
46  *              I8254_MODE5     Hardware triggered strobe (retriggerable)
47  *
48  *      In addition I8254_BCD and I8254_BINARY specify the counting mode:
49  *              I8254_BCD       BCD counting
50  *              I8254_BINARY    Binary counting
51  *
52  * comedi_8254_write()
53  *      Writes an initial value to a counter.
54  *
55  *      The largest possible initial count is 0; this is equivalent to 2^16
56  *      for binary counting and 10^4 for BCD counting.
57  *
58  *      NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
59  *      and 5 the counter "wraps around" to the highest count, either 0xffff
60  *      for binary counting or 9999 for BCD counting, and continues counting.
61  *      Modes 2 and 3 are periodic; the counter reloads itself with the initial
62  *      count and continues counting from there.
63  *
64  * comedi_8254_read()
65  *      Reads the current value from a counter.
66  *
67  * comedi_8254_status()
68  *      Reads the status of a counter.
69  *
70  * comedi_8254_load()
71  *      Sets a counters operation mode and writes the initial value.
72  *
73  * Typically the pacer clock is created by cascading two of the 16-bit counters
74  * to create a 32-bit rate generator (I8254_MODE2). These functions are
75  * provided to handle the cascaded counters:
76  *
77  * comedi_8254_ns_to_timer()
78  *      Calculates the divisor value needed for a single counter to generate
79  *      ns timing.
80  *
81  * comedi_8254_cascade_ns_to_timer()
82  *      Calculates the two divisor values needed to the generate the pacer
83  *      clock (in ns).
84  *
85  * comedi_8254_update_divisors()
86  *      Transfers the intermediate divisor values to the current divisors.
87  *
88  * comedi_8254_pacer_enable()
89  *      Programs the mode of the cascaded counters and writes the current
90  *      divisor values.
91  *
92  * To expose the counters as a subdevice for general purpose use the following
93  * functions a provided:
94  *
95  * comedi_8254_subdevice_init()
96  *      Initializes a comedi_subdevice to use the 8254 timer.
97  *
98  * comedi_8254_set_busy()
99  *      Internally flags a counter as "busy". This is done to protect the
100  *      counters that are used for the cascaded 32-bit pacer.
101  *
102  * The subdevice provides (*insn_read) and (*insn_write) operations to read
103  * the current value and write an initial value to a counter. A (*insn_config)
104  * operation is also provided to handle the following comedi instructions:
105  *
106  *      INSN_CONFIG_SET_COUNTER_MODE    calls comedi_8254_set_mode()
107  *      INSN_CONFIG_8254_READ_STATUS    calls comedi_8254_status()
108  *
109  * The (*insn_config) member of comedi_8254 can be initialized by the external
110  * driver to handle any additional instructions.
111  *
112  * NOTE: Gate control, clock routing, and any interrupt handling for the
113  * counters is not handled by this module. These features are driver dependent.
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
124 static 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
153 static 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  * comedi_8254_status - return the status of a counter
183  * @i8254:      comedi_8254 struct for the timer
184  * @counter:    the counter number
185  */
186 unsigned 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 }
198 EXPORT_SYMBOL_GPL(comedi_8254_status);
199
200 /**
201  * comedi_8254_read - read the current counter value
202  * @i8254:      comedi_8254 struct for the timer
203  * @counter:    the counter number
204  */
205 unsigned 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         /* latch counter */
213         __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
214                       I8254_CTRL_REG);
215
216         /* read LSB then MSB */
217         val = __i8254_read(i8254, counter);
218         val |= (__i8254_read(i8254, counter) << 8);
219
220         return val;
221 }
222 EXPORT_SYMBOL_GPL(comedi_8254_read);
223
224 /**
225  * comedi_8254_write - load a 16-bit initial counter value
226  * @i8254:      comedi_8254 struct for the timer
227  * @counter:    the counter number
228  * @val:        the initial value
229  */
230 void 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         /* load LSB then MSB */
241         byte = val & 0xff;
242         __i8254_write(i8254, byte, counter);
243         byte = (val >> 8) & 0xff;
244         __i8254_write(i8254, byte, counter);
245 }
246 EXPORT_SYMBOL_GPL(comedi_8254_write);
247
248 /**
249  * comedi_8254_set_mode - set the mode of a counter
250  * @i8254:      comedi_8254 struct for the timer
251  * @counter:    the counter number
252  * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
253  */
254 int 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) |    /* select counter */
265                I8254_CTRL_LSB_MSB |             /* load LSB then MSB */
266                mode;                            /* mode and BCD|binary */
267         __i8254_write(i8254, byte, I8254_CTRL_REG);
268
269         return 0;
270 }
271 EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
272
273 /**
274  * comedi_8254_load - program the mode and initial count of a counter
275  * @i8254:      comedi_8254 struct for the timer
276  * @counter:    the counter number
277  * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
278  * @val:        the initial value
279  */
280 int 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 }
295 EXPORT_SYMBOL_GPL(comedi_8254_load);
296
297 /**
298  * comedi_8254_pacer_enable - set the mode and load the cascaded counters
299  * @i8254:      comedi_8254 struct for the timer
300  * @counter1:   the counter number for the first divisor
301  * @counter2:   the counter number for the second divisor
302  * @enable:     flag to enable (load) the counters
303  */
304 void 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                  * Divisors are loaded second counter then first counter to
325                  * avoid possible issues with the first counter expiring
326                  * before the second counter is loaded.
327                  */
328                 comedi_8254_write(i8254, counter2, i8254->divisor2);
329                 comedi_8254_write(i8254, counter1, i8254->divisor1);
330         }
331 }
332 EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
333
334 /**
335  * comedi_8254_update_divisors - update the divisors for the cascaded counters
336  * @i8254:      comedi_8254 struct for the timer
337  */
338 void comedi_8254_update_divisors(struct comedi_8254 *i8254)
339 {
340         /* masking is done since counter maps zero to 0x10000 */
341         i8254->divisor = i8254->next_div & 0xffff;
342         i8254->divisor1 = i8254->next_div1 & 0xffff;
343         i8254->divisor2 = i8254->next_div2 & 0xffff;
344 }
345 EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
346
347 /**
348  * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
349  * @i8254:      comedi_8254 struct for the timer
350  * @nanosec:    the desired ns time
351  * @flags:      comedi_cmd flags
352  */
353 void 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         /* exit early if everything is already correct */
372         if (div * i8254->osc_base == *nanosec &&
373             d1 > 1 && d1 <= I8254_MAX_COUNT &&
374             d2 > 1 && d2 <= I8254_MAX_COUNT &&
375             /* check for overflow */
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 }
430 EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
431
432 /**
433  * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
434  * @i8254:      comedi_8254 struct for the timer
435  * @nanosec:    the desired ns time
436  * @flags:      comedi_cmd flags
437  */
438 void 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 }
463 EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
464
465 /**
466  * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
467  * @i8254:      comedi_8254 struct for the timer
468  * @counter:    the counter number
469  * @busy:       set/clear flag
470  */
471 void 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 }
477 EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
478
479 static 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
497 static 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
514 static 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                  * If available, call the driver provided (*insn_config)
544                  * to handle any driver implemented instructions.
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  * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
557  * @s:          comedi_subdevice struct
558  */
559 void 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 }
573 EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
574
575 static 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         /* sanity check that the iosize is valid */
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         /* default osc_base to the max speed of a generic 8254 timer */
599         i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
600
601         /* reset all the counters by setting them to I8254_MODE0 */
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  * comedi_8254_init - allocate and initialize the 8254 device for pio access
610  * @mmio:       port I/O base address
611  * @osc_base:   base time of the counter in ns
612  *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
613  * @iosize:     I/O register size
614  * @regshift:   register gap shift
615  */
616 struct 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 }
623 EXPORT_SYMBOL_GPL(comedi_8254_init);
624
625 /**
626  * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
627  * @mmio:       memory mapped I/O base address
628  * @osc_base:   base time of the counter in ns
629  *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
630  * @iosize:     I/O register size
631  * @regshift:   register gap shift
632  */
633 struct 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 }
640 EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
641
642 static int __init comedi_8254_module_init(void)
643 {
644         return 0;
645 }
646 module_init(comedi_8254_module_init);
647
648 static void __exit comedi_8254_module_exit(void)
649 {
650 }
651 module_exit(comedi_8254_module_exit);
652
653 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
654 MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
655 MODULE_LICENSE("GPL");