xref: /linux/drivers/comedi/drivers/comedi_8254.c (revision 90d25675)
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 
i8254_io8_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
i8254_io16_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
i8254_io32_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
i8254_mmio8_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
i8254_mmio16_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
i8254_mmio32_cb(struct comedi_8254 * i8254,int dir,unsigned int reg,unsigned int val)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 
__i8254_read(struct comedi_8254 * i8254,unsigned int reg)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 
__i8254_write(struct comedi_8254 * i8254,unsigned int val,unsigned int reg)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  */
comedi_8254_status(struct comedi_8254 * i8254,unsigned int counter)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  */
comedi_8254_read(struct comedi_8254 * i8254,unsigned int counter)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  */
comedi_8254_write(struct comedi_8254 * i8254,unsigned int counter,unsigned int val)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  */
comedi_8254_set_mode(struct comedi_8254 * i8254,unsigned int counter,unsigned int mode)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  */
comedi_8254_load(struct comedi_8254 * i8254,unsigned int counter,unsigned int val,unsigned int mode)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  */
comedi_8254_pacer_enable(struct comedi_8254 * i8254,unsigned int counter1,unsigned int counter2,bool enable)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  */
comedi_8254_update_divisors(struct comedi_8254 * i8254)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  */
comedi_8254_cascade_ns_to_timer(struct comedi_8254 * i8254,unsigned int * nanosec,unsigned int flags)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  */
comedi_8254_ns_to_timer(struct comedi_8254 * i8254,unsigned int * nanosec,unsigned int flags)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  */
comedi_8254_set_busy(struct comedi_8254 * i8254,unsigned int counter,bool busy)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 
comedi_8254_insn_read(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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 
comedi_8254_insn_write(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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 
comedi_8254_insn_config(struct comedi_device * dev,struct comedi_subdevice * s,struct comedi_insn * insn,unsigned int * data)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  */
comedi_8254_subdevice_init(struct comedi_subdevice * s,struct comedi_8254 * i8254)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 
__i8254_init(comedi_8254_iocb_fn * iocb,unsigned long context,unsigned int osc_base,unsigned int iosize,unsigned int regshift)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  */
comedi_8254_io_alloc(unsigned long iobase,unsigned int osc_base,unsigned int iosize,unsigned int regshift)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  */
comedi_8254_mm_alloc(void __iomem * mmio,unsigned int osc_base,unsigned int iosize,unsigned int regshift)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 
comedi_8254_module_init(void)727 static int __init comedi_8254_module_init(void)
728 {
729 	return 0;
730 }
731 module_init(comedi_8254_module_init);
732 
comedi_8254_module_exit(void)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");
741