GNU Linux-libre 6.7.9-gnu
[releases.git] / drivers / 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_io_alloc() / comedi_8254_mm_alloc()
28  *      Initializes this module to access the 8254 registers. The _mm version
29  *      sets up the module for MMIO register access; the _io version sets it
30  *      up for PIO access.  These functions return a pointer to a struct
31  *      comedi_8254 on success, or an ERR_PTR value on failure.  The pointer
32  *      returned from these functions is normally stored in the comedi_device
33  *      dev->pacer and will be freed by the comedi core during the driver
34  *      (*detach). If a driver has multiple 8254 devices, they need to be
35  *      stored in the drivers private data and freed when the driver is
36  *      detached.  If the ERR_PTR value is stored, code should check the
37  *      pointer value with !IS_ERR(pointer) before freeing.
38  *
39  *      NOTE: The counters are reset by setting them to I8254_MODE0 as part of
40  *      this initialization.
41  *
42  * comedi_8254_set_mode()
43  *      Sets a counters operation mode:
44  *              I8254_MODE0     Interrupt on terminal count
45  *              I8254_MODE1     Hardware retriggerable one-shot
46  *              I8254_MODE2     Rate generator
47  *              I8254_MODE3     Square wave mode
48  *              I8254_MODE4     Software triggered strobe
49  *              I8254_MODE5     Hardware triggered strobe (retriggerable)
50  *
51  *      In addition I8254_BCD and I8254_BINARY specify the counting mode:
52  *              I8254_BCD       BCD counting
53  *              I8254_BINARY    Binary counting
54  *
55  * comedi_8254_write()
56  *      Writes an initial value to a counter.
57  *
58  *      The largest possible initial count is 0; this is equivalent to 2^16
59  *      for binary counting and 10^4 for BCD counting.
60  *
61  *      NOTE: The counter does not stop when it reaches zero. In Mode 0, 1, 4,
62  *      and 5 the counter "wraps around" to the highest count, either 0xffff
63  *      for binary counting or 9999 for BCD counting, and continues counting.
64  *      Modes 2 and 3 are periodic; the counter reloads itself with the initial
65  *      count and continues counting from there.
66  *
67  * comedi_8254_read()
68  *      Reads the current value from a counter.
69  *
70  * comedi_8254_status()
71  *      Reads the status of a counter.
72  *
73  * comedi_8254_load()
74  *      Sets a counters operation mode and writes the initial value.
75  *
76  * Typically the pacer clock is created by cascading two of the 16-bit counters
77  * to create a 32-bit rate generator (I8254_MODE2). These functions are
78  * provided to handle the cascaded counters:
79  *
80  * comedi_8254_ns_to_timer()
81  *      Calculates the divisor value needed for a single counter to generate
82  *      ns timing.
83  *
84  * comedi_8254_cascade_ns_to_timer()
85  *      Calculates the two divisor values needed to the generate the pacer
86  *      clock (in ns).
87  *
88  * comedi_8254_update_divisors()
89  *      Transfers the intermediate divisor values to the current divisors.
90  *
91  * comedi_8254_pacer_enable()
92  *      Programs the mode of the cascaded counters and writes the current
93  *      divisor values.
94  *
95  * To expose the counters as a subdevice for general purpose use the following
96  * functions a provided:
97  *
98  * comedi_8254_subdevice_init()
99  *      Initializes a comedi_subdevice to use the 8254 timer.
100  *
101  * comedi_8254_set_busy()
102  *      Internally flags a counter as "busy". This is done to protect the
103  *      counters that are used for the cascaded 32-bit pacer.
104  *
105  * The subdevice provides (*insn_read) and (*insn_write) operations to read
106  * the current value and write an initial value to a counter. A (*insn_config)
107  * operation is also provided to handle the following comedi instructions:
108  *
109  *      INSN_CONFIG_SET_COUNTER_MODE    calls comedi_8254_set_mode()
110  *      INSN_CONFIG_8254_READ_STATUS    calls comedi_8254_status()
111  *
112  * The (*insn_config) member of comedi_8254 can be initialized by the external
113  * driver to handle any additional instructions.
114  *
115  * NOTE: Gate control, clock routing, and any interrupt handling for the
116  * counters is not handled by this module. These features are driver dependent.
117  */
118
119 #include <linux/module.h>
120 #include <linux/slab.h>
121 #include <linux/io.h>
122 #include <linux/comedi/comedidev.h>
123 #include <linux/comedi/comedi_8254.h>
124
125 #ifdef CONFIG_HAS_IOPORT
126
127 static unsigned int i8254_io8_cb(struct comedi_8254 *i8254, int dir,
128                                 unsigned int reg, unsigned int val)
129 {
130         unsigned long iobase = i8254->context;
131         unsigned int reg_offset = (reg * I8254_IO8) << i8254->regshift;
132
133         if (dir) {
134                 outb(val, iobase + reg_offset);
135                 return 0;
136         } else {
137                 return inb(iobase + reg_offset);
138         }
139 }
140
141 static unsigned int i8254_io16_cb(struct comedi_8254 *i8254, int dir,
142                                   unsigned int reg, unsigned int val)
143 {
144         unsigned long iobase = i8254->context;
145         unsigned int reg_offset = (reg * I8254_IO16) << i8254->regshift;
146
147         if (dir) {
148                 outw(val, iobase + reg_offset);
149                 return 0;
150         } else {
151                 return inw(iobase + reg_offset);
152         }
153 }
154
155 static unsigned int i8254_io32_cb(struct comedi_8254 *i8254, int dir,
156                                   unsigned int reg, unsigned int val)
157 {
158         unsigned long iobase = i8254->context;
159         unsigned int reg_offset = (reg * I8254_IO32) << i8254->regshift;
160
161         if (dir) {
162                 outl(val, iobase + reg_offset);
163                 return 0;
164         } else {
165                 return inl(iobase + reg_offset);
166         }
167 }
168
169 #endif  /* CONFIG_HAS_IOPORT */
170
171 static unsigned int i8254_mmio8_cb(struct comedi_8254 *i8254, int dir,
172                                    unsigned int reg, unsigned int val)
173 {
174         void __iomem *mmiobase = (void __iomem *)i8254->context;
175         unsigned int reg_offset = (reg * I8254_IO8) << i8254->regshift;
176
177         if (dir) {
178                 writeb(val, mmiobase + reg_offset);
179                 return 0;
180         } else {
181                 return readb(mmiobase + reg_offset);
182         }
183 }
184
185 static unsigned int i8254_mmio16_cb(struct comedi_8254 *i8254, int dir,
186                                     unsigned int reg, unsigned int val)
187 {
188         void __iomem *mmiobase = (void __iomem *)i8254->context;
189         unsigned int reg_offset = (reg * I8254_IO16) << i8254->regshift;
190
191         if (dir) {
192                 writew(val, mmiobase + reg_offset);
193                 return 0;
194         } else {
195                 return readw(mmiobase + reg_offset);
196         }
197 }
198
199 static unsigned int i8254_mmio32_cb(struct comedi_8254 *i8254, int dir,
200                                     unsigned int reg, unsigned int val)
201 {
202         void __iomem *mmiobase = (void __iomem *)i8254->context;
203         unsigned int reg_offset = (reg * I8254_IO32) << i8254->regshift;
204
205         if (dir) {
206                 writel(val, mmiobase + reg_offset);
207                 return 0;
208         } else {
209                 return readl(mmiobase + reg_offset);
210         }
211 }
212
213 static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
214 {
215         return 0xff & i8254->iocb(i8254, 0, reg, 0);
216 }
217
218 static void __i8254_write(struct comedi_8254 *i8254,
219                           unsigned int val, unsigned int reg)
220 {
221         i8254->iocb(i8254, 1, reg, val);
222 }
223
224 /**
225  * comedi_8254_status - return the status of a counter
226  * @i8254:      comedi_8254 struct for the timer
227  * @counter:    the counter number
228  */
229 unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
230 {
231         unsigned int cmd;
232
233         if (counter > 2)
234                 return 0;
235
236         cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
237         __i8254_write(i8254, cmd, I8254_CTRL_REG);
238
239         return __i8254_read(i8254, counter);
240 }
241 EXPORT_SYMBOL_GPL(comedi_8254_status);
242
243 /**
244  * comedi_8254_read - read the current counter value
245  * @i8254:      comedi_8254 struct for the timer
246  * @counter:    the counter number
247  */
248 unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
249 {
250         unsigned int val;
251
252         if (counter > 2)
253                 return 0;
254
255         /* latch counter */
256         __i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
257                       I8254_CTRL_REG);
258
259         /* read LSB then MSB */
260         val = __i8254_read(i8254, counter);
261         val |= (__i8254_read(i8254, counter) << 8);
262
263         return val;
264 }
265 EXPORT_SYMBOL_GPL(comedi_8254_read);
266
267 /**
268  * comedi_8254_write - load a 16-bit initial counter value
269  * @i8254:      comedi_8254 struct for the timer
270  * @counter:    the counter number
271  * @val:        the initial value
272  */
273 void comedi_8254_write(struct comedi_8254 *i8254,
274                        unsigned int counter, unsigned int val)
275 {
276         unsigned int byte;
277
278         if (counter > 2)
279                 return;
280         if (val > 0xffff)
281                 return;
282
283         /* load LSB then MSB */
284         byte = val & 0xff;
285         __i8254_write(i8254, byte, counter);
286         byte = (val >> 8) & 0xff;
287         __i8254_write(i8254, byte, counter);
288 }
289 EXPORT_SYMBOL_GPL(comedi_8254_write);
290
291 /**
292  * comedi_8254_set_mode - set the mode of a counter
293  * @i8254:      comedi_8254 struct for the timer
294  * @counter:    the counter number
295  * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
296  */
297 int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
298                          unsigned int mode)
299 {
300         unsigned int byte;
301
302         if (counter > 2)
303                 return -EINVAL;
304         if (mode > (I8254_MODE5 | I8254_BCD))
305                 return -EINVAL;
306
307         byte = I8254_CTRL_SEL_CTR(counter) |    /* select counter */
308                I8254_CTRL_LSB_MSB |             /* load LSB then MSB */
309                mode;                            /* mode and BCD|binary */
310         __i8254_write(i8254, byte, I8254_CTRL_REG);
311
312         return 0;
313 }
314 EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
315
316 /**
317  * comedi_8254_load - program the mode and initial count of a counter
318  * @i8254:      comedi_8254 struct for the timer
319  * @counter:    the counter number
320  * @mode:       the I8254_MODEx and I8254_BCD|I8254_BINARY
321  * @val:        the initial value
322  */
323 int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
324                      unsigned int val, unsigned int mode)
325 {
326         if (counter > 2)
327                 return -EINVAL;
328         if (val > 0xffff)
329                 return -EINVAL;
330         if (mode > (I8254_MODE5 | I8254_BCD))
331                 return -EINVAL;
332
333         comedi_8254_set_mode(i8254, counter, mode);
334         comedi_8254_write(i8254, counter, val);
335
336         return 0;
337 }
338 EXPORT_SYMBOL_GPL(comedi_8254_load);
339
340 /**
341  * comedi_8254_pacer_enable - set the mode and load the cascaded counters
342  * @i8254:      comedi_8254 struct for the timer
343  * @counter1:   the counter number for the first divisor
344  * @counter2:   the counter number for the second divisor
345  * @enable:     flag to enable (load) the counters
346  */
347 void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
348                               unsigned int counter1,
349                               unsigned int counter2,
350                               bool enable)
351 {
352         unsigned int mode;
353
354         if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
355                 return;
356
357         if (enable)
358                 mode = I8254_MODE2 | I8254_BINARY;
359         else
360                 mode = I8254_MODE0 | I8254_BINARY;
361
362         comedi_8254_set_mode(i8254, counter1, mode);
363         comedi_8254_set_mode(i8254, counter2, mode);
364
365         if (enable) {
366                 /*
367                  * Divisors are loaded second counter then first counter to
368                  * avoid possible issues with the first counter expiring
369                  * before the second counter is loaded.
370                  */
371                 comedi_8254_write(i8254, counter2, i8254->divisor2);
372                 comedi_8254_write(i8254, counter1, i8254->divisor1);
373         }
374 }
375 EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
376
377 /**
378  * comedi_8254_update_divisors - update the divisors for the cascaded counters
379  * @i8254:      comedi_8254 struct for the timer
380  */
381 void comedi_8254_update_divisors(struct comedi_8254 *i8254)
382 {
383         /* masking is done since counter maps zero to 0x10000 */
384         i8254->divisor = i8254->next_div & 0xffff;
385         i8254->divisor1 = i8254->next_div1 & 0xffff;
386         i8254->divisor2 = i8254->next_div2 & 0xffff;
387 }
388 EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
389
390 /**
391  * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
392  * @i8254:      comedi_8254 struct for the timer
393  * @nanosec:    the desired ns time
394  * @flags:      comedi_cmd flags
395  */
396 void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
397                                      unsigned int *nanosec,
398                                      unsigned int flags)
399 {
400         unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
401         unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
402         unsigned int div = d1 * d2;
403         unsigned int ns_lub = 0xffffffff;
404         unsigned int ns_glb = 0;
405         unsigned int d1_lub = 0;
406         unsigned int d1_glb = 0;
407         unsigned int d2_lub = 0;
408         unsigned int d2_glb = 0;
409         unsigned int start;
410         unsigned int ns;
411         unsigned int ns_low;
412         unsigned int ns_high;
413
414         /* exit early if everything is already correct */
415         if (div * i8254->osc_base == *nanosec &&
416             d1 > 1 && d1 <= I8254_MAX_COUNT &&
417             d2 > 1 && d2 <= I8254_MAX_COUNT &&
418             /* check for overflow */
419             div > d1 && div > d2 &&
420             div * i8254->osc_base > div &&
421             div * i8254->osc_base > i8254->osc_base)
422                 return;
423
424         div = *nanosec / i8254->osc_base;
425         d2 = I8254_MAX_COUNT;
426         start = div / d2;
427         if (start < 2)
428                 start = 2;
429         for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
430                 for (d2 = div / d1;
431                      d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
432                         ns = i8254->osc_base * d1 * d2;
433                         if (ns <= *nanosec && ns > ns_glb) {
434                                 ns_glb = ns;
435                                 d1_glb = d1;
436                                 d2_glb = d2;
437                         }
438                         if (ns >= *nanosec && ns < ns_lub) {
439                                 ns_lub = ns;
440                                 d1_lub = d1;
441                                 d2_lub = d2;
442                         }
443                 }
444         }
445
446         switch (flags & CMDF_ROUND_MASK) {
447         case CMDF_ROUND_NEAREST:
448         default:
449                 ns_high = d1_lub * d2_lub * i8254->osc_base;
450                 ns_low = d1_glb * d2_glb * i8254->osc_base;
451                 if (ns_high - *nanosec < *nanosec - ns_low) {
452                         d1 = d1_lub;
453                         d2 = d2_lub;
454                 } else {
455                         d1 = d1_glb;
456                         d2 = d2_glb;
457                 }
458                 break;
459         case CMDF_ROUND_UP:
460                 d1 = d1_lub;
461                 d2 = d2_lub;
462                 break;
463         case CMDF_ROUND_DOWN:
464                 d1 = d1_glb;
465                 d2 = d2_glb;
466                 break;
467         }
468
469         *nanosec = d1 * d2 * i8254->osc_base;
470         i8254->next_div1 = d1;
471         i8254->next_div2 = d2;
472 }
473 EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
474
475 /**
476  * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
477  * @i8254:      comedi_8254 struct for the timer
478  * @nanosec:    the desired ns time
479  * @flags:      comedi_cmd flags
480  */
481 void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
482                              unsigned int *nanosec, unsigned int flags)
483 {
484         unsigned int divisor;
485
486         switch (flags & CMDF_ROUND_MASK) {
487         default:
488         case CMDF_ROUND_NEAREST:
489                 divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
490                 break;
491         case CMDF_ROUND_UP:
492                 divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
493                 break;
494         case CMDF_ROUND_DOWN:
495                 divisor = *nanosec / i8254->osc_base;
496                 break;
497         }
498         if (divisor < 2)
499                 divisor = 2;
500         if (divisor > I8254_MAX_COUNT)
501                 divisor = I8254_MAX_COUNT;
502
503         *nanosec = divisor * i8254->osc_base;
504         i8254->next_div = divisor;
505 }
506 EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
507
508 /**
509  * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
510  * @i8254:      comedi_8254 struct for the timer
511  * @counter:    the counter number
512  * @busy:       set/clear flag
513  */
514 void comedi_8254_set_busy(struct comedi_8254 *i8254,
515                           unsigned int counter, bool busy)
516 {
517         if (counter < 3)
518                 i8254->busy[counter] = busy;
519 }
520 EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
521
522 static int comedi_8254_insn_read(struct comedi_device *dev,
523                                  struct comedi_subdevice *s,
524                                  struct comedi_insn *insn,
525                                  unsigned int *data)
526 {
527         struct comedi_8254 *i8254 = s->private;
528         unsigned int chan = CR_CHAN(insn->chanspec);
529         int i;
530
531         if (i8254->busy[chan])
532                 return -EBUSY;
533
534         for (i = 0; i < insn->n; i++)
535                 data[i] = comedi_8254_read(i8254, chan);
536
537         return insn->n;
538 }
539
540 static int comedi_8254_insn_write(struct comedi_device *dev,
541                                   struct comedi_subdevice *s,
542                                   struct comedi_insn *insn,
543                                   unsigned int *data)
544 {
545         struct comedi_8254 *i8254 = s->private;
546         unsigned int chan = CR_CHAN(insn->chanspec);
547
548         if (i8254->busy[chan])
549                 return -EBUSY;
550
551         if (insn->n)
552                 comedi_8254_write(i8254, chan, data[insn->n - 1]);
553
554         return insn->n;
555 }
556
557 static int comedi_8254_insn_config(struct comedi_device *dev,
558                                    struct comedi_subdevice *s,
559                                    struct comedi_insn *insn,
560                                    unsigned int *data)
561 {
562         struct comedi_8254 *i8254 = s->private;
563         unsigned int chan = CR_CHAN(insn->chanspec);
564         int ret;
565
566         if (i8254->busy[chan])
567                 return -EBUSY;
568
569         switch (data[0]) {
570         case INSN_CONFIG_RESET:
571                 ret = comedi_8254_set_mode(i8254, chan,
572                                            I8254_MODE0 | I8254_BINARY);
573                 if (ret)
574                         return ret;
575                 break;
576         case INSN_CONFIG_SET_COUNTER_MODE:
577                 ret = comedi_8254_set_mode(i8254, chan, data[1]);
578                 if (ret)
579                         return ret;
580                 break;
581         case INSN_CONFIG_8254_READ_STATUS:
582                 data[1] = comedi_8254_status(i8254, chan);
583                 break;
584         default:
585                 /*
586                  * If available, call the driver provided (*insn_config)
587                  * to handle any driver implemented instructions.
588                  */
589                 if (i8254->insn_config)
590                         return i8254->insn_config(dev, s, insn, data);
591
592                 return -EINVAL;
593         }
594
595         return insn->n;
596 }
597
598 /**
599  * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
600  * @s:          comedi_subdevice struct
601  * @i8254:      comedi_8254 struct
602  */
603 void comedi_8254_subdevice_init(struct comedi_subdevice *s,
604                                 struct comedi_8254 *i8254)
605 {
606         s->type         = COMEDI_SUBD_COUNTER;
607         s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
608         s->n_chan       = 3;
609         s->maxdata      = 0xffff;
610         s->range_table  = &range_unknown;
611         s->insn_read    = comedi_8254_insn_read;
612         s->insn_write   = comedi_8254_insn_write;
613         s->insn_config  = comedi_8254_insn_config;
614
615         s->private      = i8254;
616 }
617 EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
618
619 static struct comedi_8254 *__i8254_init(comedi_8254_iocb_fn *iocb,
620                                         unsigned long context,
621                                         unsigned int osc_base,
622                                         unsigned int iosize,
623                                         unsigned int regshift)
624 {
625         struct comedi_8254 *i8254;
626         int i;
627
628         /* sanity check that the iosize is valid */
629         if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
630               iosize == I8254_IO32))
631                 return ERR_PTR(-EINVAL);
632
633         if (!iocb)
634                 return ERR_PTR(-EINVAL);
635
636         i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
637         if (!i8254)
638                 return ERR_PTR(-ENOMEM);
639
640         i8254->iocb     = iocb;
641         i8254->context  = context;
642         i8254->iosize   = iosize;
643         i8254->regshift = regshift;
644
645         /* default osc_base to the max speed of a generic 8254 timer */
646         i8254->osc_base = osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
647
648         /* reset all the counters by setting them to I8254_MODE0 */
649         for (i = 0; i < 3; i++)
650                 comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
651
652         return i8254;
653 }
654
655 #ifdef CONFIG_HAS_IOPORT
656
657 /**
658  * comedi_8254_io_alloc - allocate and initialize the 8254 device for pio access
659  * @iobase:     port I/O base address
660  * @osc_base:   base time of the counter in ns
661  *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
662  * @iosize:     I/O register size
663  * @regshift:   register gap shift
664  *
665  * Return: A pointer to a struct comedi_8254 or an ERR_PTR value.
666  */
667 struct comedi_8254 *comedi_8254_io_alloc(unsigned long iobase,
668                                          unsigned int osc_base,
669                                          unsigned int iosize,
670                                          unsigned int regshift)
671 {
672         comedi_8254_iocb_fn *iocb;
673
674         switch (iosize) {
675         case I8254_IO8:
676                 iocb = i8254_io8_cb;
677                 break;
678         case I8254_IO16:
679                 iocb = i8254_io16_cb;
680                 break;
681         case I8254_IO32:
682                 iocb = i8254_io32_cb;
683                 break;
684         default:
685                 return ERR_PTR(-EINVAL);
686         }
687         return __i8254_init(iocb, iobase, osc_base, iosize, regshift);
688 }
689 EXPORT_SYMBOL_GPL(comedi_8254_io_alloc);
690
691 #endif  /* CONFIG_HAS_IOPORT */
692
693 /**
694  * comedi_8254_mm_alloc - allocate and initialize the 8254 device for mmio access
695  * @mmio:       memory mapped I/O base address
696  * @osc_base:   base time of the counter in ns
697  *              OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
698  * @iosize:     I/O register size
699  * @regshift:   register gap shift
700  *
701  * Return: A pointer to a struct comedi_8254 or an ERR_PTR value.
702  */
703 struct comedi_8254 *comedi_8254_mm_alloc(void __iomem *mmio,
704                                          unsigned int osc_base,
705                                          unsigned int iosize,
706                                          unsigned int regshift)
707 {
708         comedi_8254_iocb_fn *iocb;
709
710         switch (iosize) {
711         case I8254_IO8:
712                 iocb = i8254_mmio8_cb;
713                 break;
714         case I8254_IO16:
715                 iocb = i8254_mmio16_cb;
716                 break;
717         case I8254_IO32:
718                 iocb = i8254_mmio32_cb;
719                 break;
720         default:
721                 return ERR_PTR(-EINVAL);
722         }
723         return __i8254_init(iocb, (unsigned long)mmio, osc_base, iosize, regshift);
724 }
725 EXPORT_SYMBOL_GPL(comedi_8254_mm_alloc);
726
727 static int __init comedi_8254_module_init(void)
728 {
729         return 0;
730 }
731 module_init(comedi_8254_module_init);
732
733 static void __exit comedi_8254_module_exit(void)
734 {
735 }
736 module_exit(comedi_8254_module_exit);
737
738 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
739 MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
740 MODULE_LICENSE("GPL");