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 #include <linux/comedi/comedidev.h>
120 #include <linux/comedi/comedi_8254.h>
121 
__i8254_read(struct comedi_8254 * i8254,unsigned int reg)122 static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg)
123 {
124 	unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
125 	unsigned int val;
126 
127 	switch (i8254->iosize) {
128 	default:
129 	case I8254_IO8:
130 		if (i8254->mmio)
131 			val = readb(i8254->mmio + reg_offset);
132 		else
133 			val = inb(i8254->iobase + reg_offset);
134 		break;
135 	case I8254_IO16:
136 		if (i8254->mmio)
137 			val = readw(i8254->mmio + reg_offset);
138 		else
139 			val = inw(i8254->iobase + reg_offset);
140 		break;
141 	case I8254_IO32:
142 		if (i8254->mmio)
143 			val = readl(i8254->mmio + reg_offset);
144 		else
145 			val = inl(i8254->iobase + reg_offset);
146 		break;
147 	}
148 	return val & 0xff;
149 }
150 
__i8254_write(struct comedi_8254 * i8254,unsigned int val,unsigned int reg)151 static void __i8254_write(struct comedi_8254 *i8254,
152 			  unsigned int val, unsigned int reg)
153 {
154 	unsigned int reg_offset = (reg * i8254->iosize) << i8254->regshift;
155 
156 	switch (i8254->iosize) {
157 	default:
158 	case I8254_IO8:
159 		if (i8254->mmio)
160 			writeb(val, i8254->mmio + reg_offset);
161 		else
162 			outb(val, i8254->iobase + reg_offset);
163 		break;
164 	case I8254_IO16:
165 		if (i8254->mmio)
166 			writew(val, i8254->mmio + reg_offset);
167 		else
168 			outw(val, i8254->iobase + reg_offset);
169 		break;
170 	case I8254_IO32:
171 		if (i8254->mmio)
172 			writel(val, i8254->mmio + reg_offset);
173 		else
174 			outl(val, i8254->iobase + reg_offset);
175 		break;
176 	}
177 }
178 
179 /**
180  * comedi_8254_status - return the status of a counter
181  * @i8254:	comedi_8254 struct for the timer
182  * @counter:	the counter number
183  */
comedi_8254_status(struct comedi_8254 * i8254,unsigned int counter)184 unsigned int comedi_8254_status(struct comedi_8254 *i8254, unsigned int counter)
185 {
186 	unsigned int cmd;
187 
188 	if (counter > 2)
189 		return 0;
190 
191 	cmd = I8254_CTRL_READBACK_STATUS | I8254_CTRL_READBACK_SEL_CTR(counter);
192 	__i8254_write(i8254, cmd, I8254_CTRL_REG);
193 
194 	return __i8254_read(i8254, counter);
195 }
196 EXPORT_SYMBOL_GPL(comedi_8254_status);
197 
198 /**
199  * comedi_8254_read - read the current counter value
200  * @i8254:	comedi_8254 struct for the timer
201  * @counter:	the counter number
202  */
comedi_8254_read(struct comedi_8254 * i8254,unsigned int counter)203 unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter)
204 {
205 	unsigned int val;
206 
207 	if (counter > 2)
208 		return 0;
209 
210 	/* latch counter */
211 	__i8254_write(i8254, I8254_CTRL_SEL_CTR(counter) | I8254_CTRL_LATCH,
212 		      I8254_CTRL_REG);
213 
214 	/* read LSB then MSB */
215 	val = __i8254_read(i8254, counter);
216 	val |= (__i8254_read(i8254, counter) << 8);
217 
218 	return val;
219 }
220 EXPORT_SYMBOL_GPL(comedi_8254_read);
221 
222 /**
223  * comedi_8254_write - load a 16-bit initial counter value
224  * @i8254:	comedi_8254 struct for the timer
225  * @counter:	the counter number
226  * @val:	the initial value
227  */
comedi_8254_write(struct comedi_8254 * i8254,unsigned int counter,unsigned int val)228 void comedi_8254_write(struct comedi_8254 *i8254,
229 		       unsigned int counter, unsigned int val)
230 {
231 	unsigned int byte;
232 
233 	if (counter > 2)
234 		return;
235 	if (val > 0xffff)
236 		return;
237 
238 	/* load LSB then MSB */
239 	byte = val & 0xff;
240 	__i8254_write(i8254, byte, counter);
241 	byte = (val >> 8) & 0xff;
242 	__i8254_write(i8254, byte, counter);
243 }
244 EXPORT_SYMBOL_GPL(comedi_8254_write);
245 
246 /**
247  * comedi_8254_set_mode - set the mode of a counter
248  * @i8254:	comedi_8254 struct for the timer
249  * @counter:	the counter number
250  * @mode:	the I8254_MODEx and I8254_BCD|I8254_BINARY
251  */
comedi_8254_set_mode(struct comedi_8254 * i8254,unsigned int counter,unsigned int mode)252 int comedi_8254_set_mode(struct comedi_8254 *i8254, unsigned int counter,
253 			 unsigned int mode)
254 {
255 	unsigned int byte;
256 
257 	if (counter > 2)
258 		return -EINVAL;
259 	if (mode > (I8254_MODE5 | I8254_BCD))
260 		return -EINVAL;
261 
262 	byte = I8254_CTRL_SEL_CTR(counter) |	/* select counter */
263 	       I8254_CTRL_LSB_MSB |		/* load LSB then MSB */
264 	       mode;				/* mode and BCD|binary */
265 	__i8254_write(i8254, byte, I8254_CTRL_REG);
266 
267 	return 0;
268 }
269 EXPORT_SYMBOL_GPL(comedi_8254_set_mode);
270 
271 /**
272  * comedi_8254_load - program the mode and initial count of a counter
273  * @i8254:	comedi_8254 struct for the timer
274  * @counter:	the counter number
275  * @mode:	the I8254_MODEx and I8254_BCD|I8254_BINARY
276  * @val:	the initial value
277  */
comedi_8254_load(struct comedi_8254 * i8254,unsigned int counter,unsigned int val,unsigned int mode)278 int comedi_8254_load(struct comedi_8254 *i8254, unsigned int counter,
279 		     unsigned int val, unsigned int mode)
280 {
281 	if (counter > 2)
282 		return -EINVAL;
283 	if (val > 0xffff)
284 		return -EINVAL;
285 	if (mode > (I8254_MODE5 | I8254_BCD))
286 		return -EINVAL;
287 
288 	comedi_8254_set_mode(i8254, counter, mode);
289 	comedi_8254_write(i8254, counter, val);
290 
291 	return 0;
292 }
293 EXPORT_SYMBOL_GPL(comedi_8254_load);
294 
295 /**
296  * comedi_8254_pacer_enable - set the mode and load the cascaded counters
297  * @i8254:	comedi_8254 struct for the timer
298  * @counter1:	the counter number for the first divisor
299  * @counter2:	the counter number for the second divisor
300  * @enable:	flag to enable (load) the counters
301  */
comedi_8254_pacer_enable(struct comedi_8254 * i8254,unsigned int counter1,unsigned int counter2,bool enable)302 void comedi_8254_pacer_enable(struct comedi_8254 *i8254,
303 			      unsigned int counter1,
304 			      unsigned int counter2,
305 			      bool enable)
306 {
307 	unsigned int mode;
308 
309 	if (counter1 > 2 || counter2 > 2 || counter1 == counter2)
310 		return;
311 
312 	if (enable)
313 		mode = I8254_MODE2 | I8254_BINARY;
314 	else
315 		mode = I8254_MODE0 | I8254_BINARY;
316 
317 	comedi_8254_set_mode(i8254, counter1, mode);
318 	comedi_8254_set_mode(i8254, counter2, mode);
319 
320 	if (enable) {
321 		/*
322 		 * Divisors are loaded second counter then first counter to
323 		 * avoid possible issues with the first counter expiring
324 		 * before the second counter is loaded.
325 		 */
326 		comedi_8254_write(i8254, counter2, i8254->divisor2);
327 		comedi_8254_write(i8254, counter1, i8254->divisor1);
328 	}
329 }
330 EXPORT_SYMBOL_GPL(comedi_8254_pacer_enable);
331 
332 /**
333  * comedi_8254_update_divisors - update the divisors for the cascaded counters
334  * @i8254:	comedi_8254 struct for the timer
335  */
comedi_8254_update_divisors(struct comedi_8254 * i8254)336 void comedi_8254_update_divisors(struct comedi_8254 *i8254)
337 {
338 	/* masking is done since counter maps zero to 0x10000 */
339 	i8254->divisor = i8254->next_div & 0xffff;
340 	i8254->divisor1 = i8254->next_div1 & 0xffff;
341 	i8254->divisor2 = i8254->next_div2 & 0xffff;
342 }
343 EXPORT_SYMBOL_GPL(comedi_8254_update_divisors);
344 
345 /**
346  * comedi_8254_cascade_ns_to_timer - calculate the cascaded divisor values
347  * @i8254:	comedi_8254 struct for the timer
348  * @nanosec:	the desired ns time
349  * @flags:	comedi_cmd flags
350  */
comedi_8254_cascade_ns_to_timer(struct comedi_8254 * i8254,unsigned int * nanosec,unsigned int flags)351 void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254,
352 				     unsigned int *nanosec,
353 				     unsigned int flags)
354 {
355 	unsigned int d1 = i8254->next_div1 ? i8254->next_div1 : I8254_MAX_COUNT;
356 	unsigned int d2 = i8254->next_div2 ? i8254->next_div2 : I8254_MAX_COUNT;
357 	unsigned int div = d1 * d2;
358 	unsigned int ns_lub = 0xffffffff;
359 	unsigned int ns_glb = 0;
360 	unsigned int d1_lub = 0;
361 	unsigned int d1_glb = 0;
362 	unsigned int d2_lub = 0;
363 	unsigned int d2_glb = 0;
364 	unsigned int start;
365 	unsigned int ns;
366 	unsigned int ns_low;
367 	unsigned int ns_high;
368 
369 	/* exit early if everything is already correct */
370 	if (div * i8254->osc_base == *nanosec &&
371 	    d1 > 1 && d1 <= I8254_MAX_COUNT &&
372 	    d2 > 1 && d2 <= I8254_MAX_COUNT &&
373 	    /* check for overflow */
374 	    div > d1 && div > d2 &&
375 	    div * i8254->osc_base > div &&
376 	    div * i8254->osc_base > i8254->osc_base)
377 		return;
378 
379 	div = *nanosec / i8254->osc_base;
380 	d2 = I8254_MAX_COUNT;
381 	start = div / d2;
382 	if (start < 2)
383 		start = 2;
384 	for (d1 = start; d1 <= div / d1 + 1 && d1 <= I8254_MAX_COUNT; d1++) {
385 		for (d2 = div / d1;
386 		     d1 * d2 <= div + d1 + 1 && d2 <= I8254_MAX_COUNT; d2++) {
387 			ns = i8254->osc_base * d1 * d2;
388 			if (ns <= *nanosec && ns > ns_glb) {
389 				ns_glb = ns;
390 				d1_glb = d1;
391 				d2_glb = d2;
392 			}
393 			if (ns >= *nanosec && ns < ns_lub) {
394 				ns_lub = ns;
395 				d1_lub = d1;
396 				d2_lub = d2;
397 			}
398 		}
399 	}
400 
401 	switch (flags & CMDF_ROUND_MASK) {
402 	case CMDF_ROUND_NEAREST:
403 	default:
404 		ns_high = d1_lub * d2_lub * i8254->osc_base;
405 		ns_low = d1_glb * d2_glb * i8254->osc_base;
406 		if (ns_high - *nanosec < *nanosec - ns_low) {
407 			d1 = d1_lub;
408 			d2 = d2_lub;
409 		} else {
410 			d1 = d1_glb;
411 			d2 = d2_glb;
412 		}
413 		break;
414 	case CMDF_ROUND_UP:
415 		d1 = d1_lub;
416 		d2 = d2_lub;
417 		break;
418 	case CMDF_ROUND_DOWN:
419 		d1 = d1_glb;
420 		d2 = d2_glb;
421 		break;
422 	}
423 
424 	*nanosec = d1 * d2 * i8254->osc_base;
425 	i8254->next_div1 = d1;
426 	i8254->next_div2 = d2;
427 }
428 EXPORT_SYMBOL_GPL(comedi_8254_cascade_ns_to_timer);
429 
430 /**
431  * comedi_8254_ns_to_timer - calculate the divisor value for nanosec timing
432  * @i8254:	comedi_8254 struct for the timer
433  * @nanosec:	the desired ns time
434  * @flags:	comedi_cmd flags
435  */
comedi_8254_ns_to_timer(struct comedi_8254 * i8254,unsigned int * nanosec,unsigned int flags)436 void comedi_8254_ns_to_timer(struct comedi_8254 *i8254,
437 			     unsigned int *nanosec, unsigned int flags)
438 {
439 	unsigned int divisor;
440 
441 	switch (flags & CMDF_ROUND_MASK) {
442 	default:
443 	case CMDF_ROUND_NEAREST:
444 		divisor = DIV_ROUND_CLOSEST(*nanosec, i8254->osc_base);
445 		break;
446 	case CMDF_ROUND_UP:
447 		divisor = DIV_ROUND_UP(*nanosec, i8254->osc_base);
448 		break;
449 	case CMDF_ROUND_DOWN:
450 		divisor = *nanosec / i8254->osc_base;
451 		break;
452 	}
453 	if (divisor < 2)
454 		divisor = 2;
455 	if (divisor > I8254_MAX_COUNT)
456 		divisor = I8254_MAX_COUNT;
457 
458 	*nanosec = divisor * i8254->osc_base;
459 	i8254->next_div = divisor;
460 }
461 EXPORT_SYMBOL_GPL(comedi_8254_ns_to_timer);
462 
463 /**
464  * comedi_8254_set_busy - set/clear the "busy" flag for a given counter
465  * @i8254:	comedi_8254 struct for the timer
466  * @counter:	the counter number
467  * @busy:	set/clear flag
468  */
comedi_8254_set_busy(struct comedi_8254 * i8254,unsigned int counter,bool busy)469 void comedi_8254_set_busy(struct comedi_8254 *i8254,
470 			  unsigned int counter, bool busy)
471 {
472 	if (counter < 3)
473 		i8254->busy[counter] = busy;
474 }
475 EXPORT_SYMBOL_GPL(comedi_8254_set_busy);
476 
comedi_8254_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)477 static int comedi_8254_insn_read(struct comedi_device *dev,
478 				 struct comedi_subdevice *s,
479 				 struct comedi_insn *insn,
480 				 unsigned int *data)
481 {
482 	struct comedi_8254 *i8254 = s->private;
483 	unsigned int chan = CR_CHAN(insn->chanspec);
484 	int i;
485 
486 	if (i8254->busy[chan])
487 		return -EBUSY;
488 
489 	for (i = 0; i < insn->n; i++)
490 		data[i] = comedi_8254_read(i8254, chan);
491 
492 	return insn->n;
493 }
494 
comedi_8254_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)495 static int comedi_8254_insn_write(struct comedi_device *dev,
496 				  struct comedi_subdevice *s,
497 				  struct comedi_insn *insn,
498 				  unsigned int *data)
499 {
500 	struct comedi_8254 *i8254 = s->private;
501 	unsigned int chan = CR_CHAN(insn->chanspec);
502 
503 	if (i8254->busy[chan])
504 		return -EBUSY;
505 
506 	if (insn->n)
507 		comedi_8254_write(i8254, chan, data[insn->n - 1]);
508 
509 	return insn->n;
510 }
511 
comedi_8254_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)512 static int comedi_8254_insn_config(struct comedi_device *dev,
513 				   struct comedi_subdevice *s,
514 				   struct comedi_insn *insn,
515 				   unsigned int *data)
516 {
517 	struct comedi_8254 *i8254 = s->private;
518 	unsigned int chan = CR_CHAN(insn->chanspec);
519 	int ret;
520 
521 	if (i8254->busy[chan])
522 		return -EBUSY;
523 
524 	switch (data[0]) {
525 	case INSN_CONFIG_RESET:
526 		ret = comedi_8254_set_mode(i8254, chan,
527 					   I8254_MODE0 | I8254_BINARY);
528 		if (ret)
529 			return ret;
530 		break;
531 	case INSN_CONFIG_SET_COUNTER_MODE:
532 		ret = comedi_8254_set_mode(i8254, chan, data[1]);
533 		if (ret)
534 			return ret;
535 		break;
536 	case INSN_CONFIG_8254_READ_STATUS:
537 		data[1] = comedi_8254_status(i8254, chan);
538 		break;
539 	default:
540 		/*
541 		 * If available, call the driver provided (*insn_config)
542 		 * to handle any driver implemented instructions.
543 		 */
544 		if (i8254->insn_config)
545 			return i8254->insn_config(dev, s, insn, data);
546 
547 		return -EINVAL;
548 	}
549 
550 	return insn->n;
551 }
552 
553 /**
554  * comedi_8254_subdevice_init - initialize a comedi_subdevice for the 8254 timer
555  * @s:		comedi_subdevice struct
556  * @i8254:	comedi_8254 struct
557  */
comedi_8254_subdevice_init(struct comedi_subdevice * s,struct comedi_8254 * i8254)558 void comedi_8254_subdevice_init(struct comedi_subdevice *s,
559 				struct comedi_8254 *i8254)
560 {
561 	s->type		= COMEDI_SUBD_COUNTER;
562 	s->subdev_flags	= SDF_READABLE | SDF_WRITABLE;
563 	s->n_chan	= 3;
564 	s->maxdata	= 0xffff;
565 	s->range_table	= &range_unknown;
566 	s->insn_read	= comedi_8254_insn_read;
567 	s->insn_write	= comedi_8254_insn_write;
568 	s->insn_config	= comedi_8254_insn_config;
569 
570 	s->private	= i8254;
571 }
572 EXPORT_SYMBOL_GPL(comedi_8254_subdevice_init);
573 
__i8254_init(unsigned long iobase,void __iomem * mmio,unsigned int osc_base,unsigned int iosize,unsigned int regshift)574 static struct comedi_8254 *__i8254_init(unsigned long iobase,
575 					void __iomem *mmio,
576 					unsigned int osc_base,
577 					unsigned int iosize,
578 					unsigned int regshift)
579 {
580 	struct comedi_8254 *i8254;
581 	int i;
582 
583 	/* sanity check that the iosize is valid */
584 	if (!(iosize == I8254_IO8 || iosize == I8254_IO16 ||
585 	      iosize == I8254_IO32))
586 		return NULL;
587 
588 	i8254 = kzalloc(sizeof(*i8254), GFP_KERNEL);
589 	if (!i8254)
590 		return NULL;
591 
592 	i8254->iobase	= iobase;
593 	i8254->mmio	= mmio;
594 	i8254->iosize	= iosize;
595 	i8254->regshift	= regshift;
596 
597 	/* default osc_base to the max speed of a generic 8254 timer */
598 	i8254->osc_base	= osc_base ? osc_base : I8254_OSC_BASE_10MHZ;
599 
600 	/* reset all the counters by setting them to I8254_MODE0 */
601 	for (i = 0; i < 3; i++)
602 		comedi_8254_set_mode(i8254, i, I8254_MODE0 | I8254_BINARY);
603 
604 	return i8254;
605 }
606 
607 /**
608  * comedi_8254_init - allocate and initialize the 8254 device for pio access
609  * @iobase:	port I/O base address
610  * @osc_base:	base time of the counter in ns
611  *		OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
612  * @iosize:	I/O register size
613  * @regshift:	register gap shift
614  */
comedi_8254_init(unsigned long iobase,unsigned int osc_base,unsigned int iosize,unsigned int regshift)615 struct comedi_8254 *comedi_8254_init(unsigned long iobase,
616 				     unsigned int osc_base,
617 				     unsigned int iosize,
618 				     unsigned int regshift)
619 {
620 	return __i8254_init(iobase, NULL, osc_base, iosize, regshift);
621 }
622 EXPORT_SYMBOL_GPL(comedi_8254_init);
623 
624 /**
625  * comedi_8254_mm_init - allocate and initialize the 8254 device for mmio access
626  * @mmio:	memory mapped I/O base address
627  * @osc_base:	base time of the counter in ns
628  *		OPTIONAL - only used by comedi_8254_cascade_ns_to_timer()
629  * @iosize:	I/O register size
630  * @regshift:	register gap shift
631  */
comedi_8254_mm_init(void __iomem * mmio,unsigned int osc_base,unsigned int iosize,unsigned int regshift)632 struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio,
633 					unsigned int osc_base,
634 					unsigned int iosize,
635 					unsigned int regshift)
636 {
637 	return __i8254_init(0, mmio, osc_base, iosize, regshift);
638 }
639 EXPORT_SYMBOL_GPL(comedi_8254_mm_init);
640 
comedi_8254_module_init(void)641 static int __init comedi_8254_module_init(void)
642 {
643 	return 0;
644 }
645 module_init(comedi_8254_module_init);
646 
comedi_8254_module_exit(void)647 static void __exit comedi_8254_module_exit(void)
648 {
649 }
650 module_exit(comedi_8254_module_exit);
651 
652 MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
653 MODULE_DESCRIPTION("Comedi: Generic 8254 timer/counter support");
654 MODULE_LICENSE("GPL");
655