1703afc38a0e79b69ecc8529924848e020a586e70David Schleef/*
2703afc38a0e79b69ecc8529924848e020a586e70David Schleef    comedi/drivers/8253.h
3703afc38a0e79b69ecc8529924848e020a586e70David Schleef    Header file for 8253
4703afc38a0e79b69ecc8529924848e020a586e70David Schleef
5703afc38a0e79b69ecc8529924848e020a586e70David Schleef    COMEDI - Linux Control and Measurement Device Interface
6703afc38a0e79b69ecc8529924848e020a586e70David Schleef    Copyright (C) 2000 David A. Schleef <ds@schleef.org>
7703afc38a0e79b69ecc8529924848e020a586e70David Schleef
8703afc38a0e79b69ecc8529924848e020a586e70David Schleef    This program is free software; you can redistribute it and/or modify
9703afc38a0e79b69ecc8529924848e020a586e70David Schleef    it under the terms of the GNU General Public License as published by
10703afc38a0e79b69ecc8529924848e020a586e70David Schleef    the Free Software Foundation; either version 2 of the License, or
11703afc38a0e79b69ecc8529924848e020a586e70David Schleef    (at your option) any later version.
12703afc38a0e79b69ecc8529924848e020a586e70David Schleef
13703afc38a0e79b69ecc8529924848e020a586e70David Schleef    This program is distributed in the hope that it will be useful,
14703afc38a0e79b69ecc8529924848e020a586e70David Schleef    but WITHOUT ANY WARRANTY; without even the implied warranty of
15703afc38a0e79b69ecc8529924848e020a586e70David Schleef    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16703afc38a0e79b69ecc8529924848e020a586e70David Schleef    GNU General Public License for more details.
17703afc38a0e79b69ecc8529924848e020a586e70David Schleef
18703afc38a0e79b69ecc8529924848e020a586e70David Schleef    You should have received a copy of the GNU General Public License
19703afc38a0e79b69ecc8529924848e020a586e70David Schleef    along with this program; if not, write to the Free Software
20703afc38a0e79b69ecc8529924848e020a586e70David Schleef    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21703afc38a0e79b69ecc8529924848e020a586e70David Schleef
22703afc38a0e79b69ecc8529924848e020a586e70David Schleef*/
23703afc38a0e79b69ecc8529924848e020a586e70David Schleef
24703afc38a0e79b69ecc8529924848e020a586e70David Schleef#ifndef _8253_H
25703afc38a0e79b69ecc8529924848e020a586e70David Schleef#define _8253_H
26703afc38a0e79b69ecc8529924848e020a586e70David Schleef
27703afc38a0e79b69ecc8529924848e020a586e70David Schleef#include "../comedi.h"
28703afc38a0e79b69ecc8529924848e020a586e70David Schleef
29703afc38a0e79b69ecc8529924848e020a586e70David Schleef#define i8253_cascade_ns_to_timer i8253_cascade_ns_to_timer_2div
30703afc38a0e79b69ecc8529924848e020a586e70David Schleef
31703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline void i8253_cascade_ns_to_timer_2div_old(int i8253_osc_base,
320a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						      unsigned int *d1,
330a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						      unsigned int *d2,
340a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						      unsigned int *nanosec,
350a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						      int round_mode)
36703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
37703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int divider;
38703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int div1, div2;
39703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int div1_glb, div2_glb, ns_glb;
40703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int div1_lub, div2_lub, ns_lub;
41703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int ns;
42703afc38a0e79b69ecc8529924848e020a586e70David Schleef
43703afc38a0e79b69ecc8529924848e020a586e70David Schleef	divider = (*nanosec + i8253_osc_base / 2) / i8253_osc_base;
44703afc38a0e79b69ecc8529924848e020a586e70David Schleef
45703afc38a0e79b69ecc8529924848e020a586e70David Schleef	/* find 2 integers 1<={x,y}<=65536 such that x*y is
46703afc38a0e79b69ecc8529924848e020a586e70David Schleef	   close to divider */
47703afc38a0e79b69ecc8529924848e020a586e70David Schleef
48703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1_lub = div2_lub = 0;
49703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1_glb = div2_glb = 0;
50703afc38a0e79b69ecc8529924848e020a586e70David Schleef
51703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ns_glb = 0;
52703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ns_lub = 0xffffffff;
53703afc38a0e79b69ecc8529924848e020a586e70David Schleef
54703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div2 = 0x10000;
55703afc38a0e79b69ecc8529924848e020a586e70David Schleef	for (div1 = divider / 65536 + 1; div1 < div2; div1++) {
56703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div2 = divider / div1;
57703afc38a0e79b69ecc8529924848e020a586e70David Schleef
58703afc38a0e79b69ecc8529924848e020a586e70David Schleef		ns = i8253_osc_base * div1 * div2;
59703afc38a0e79b69ecc8529924848e020a586e70David Schleef		if (ns <= *nanosec && ns > ns_glb) {
60703afc38a0e79b69ecc8529924848e020a586e70David Schleef			ns_glb = ns;
61703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div1_glb = div1;
62703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2_glb = div2;
63703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
64703afc38a0e79b69ecc8529924848e020a586e70David Schleef
65703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div2++;
66703afc38a0e79b69ecc8529924848e020a586e70David Schleef		if (div2 <= 65536) {
67703afc38a0e79b69ecc8529924848e020a586e70David Schleef			ns = i8253_osc_base * div1 * div2;
68703afc38a0e79b69ecc8529924848e020a586e70David Schleef			if (ns > *nanosec && ns < ns_lub) {
69703afc38a0e79b69ecc8529924848e020a586e70David Schleef				ns_lub = ns;
70703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div1_lub = div1;
71703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div2_lub = div2;
72703afc38a0e79b69ecc8529924848e020a586e70David Schleef			}
73703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
74703afc38a0e79b69ecc8529924848e020a586e70David Schleef	}
75703afc38a0e79b69ecc8529924848e020a586e70David Schleef
76703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*nanosec = div1_lub * div2_lub * i8253_osc_base;
77703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*d1 = div1_lub & 0xffff;
78703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*d2 = div2_lub & 0xffff;
79703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return;
80703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
81703afc38a0e79b69ecc8529924848e020a586e70David Schleef
82703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline void i8253_cascade_ns_to_timer_power(int i8253_osc_base,
830a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						   unsigned int *d1,
840a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						   unsigned int *d2,
850a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						   unsigned int *nanosec,
860a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						   int round_mode)
87703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
88703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int div1, div2;
89703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int base;
90703afc38a0e79b69ecc8529924848e020a586e70David Schleef
91703afc38a0e79b69ecc8529924848e020a586e70David Schleef	for (div1 = 2; div1 <= (1 << 16); div1 <<= 1) {
92703afc38a0e79b69ecc8529924848e020a586e70David Schleef		base = i8253_osc_base * div1;
93703afc38a0e79b69ecc8529924848e020a586e70David Schleef		round_mode &= TRIG_ROUND_MASK;
94703afc38a0e79b69ecc8529924848e020a586e70David Schleef		switch (round_mode) {
95703afc38a0e79b69ecc8529924848e020a586e70David Schleef		case TRIG_ROUND_NEAREST:
96703afc38a0e79b69ecc8529924848e020a586e70David Schleef		default:
97703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = (*nanosec + base / 2) / base;
98703afc38a0e79b69ecc8529924848e020a586e70David Schleef			break;
99703afc38a0e79b69ecc8529924848e020a586e70David Schleef		case TRIG_ROUND_DOWN:
100703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = (*nanosec) / base;
101703afc38a0e79b69ecc8529924848e020a586e70David Schleef			break;
102703afc38a0e79b69ecc8529924848e020a586e70David Schleef		case TRIG_ROUND_UP:
103703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = (*nanosec + base - 1) / base;
104703afc38a0e79b69ecc8529924848e020a586e70David Schleef			break;
105703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
106703afc38a0e79b69ecc8529924848e020a586e70David Schleef		if (div2 < 2)
107703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = 2;
108703afc38a0e79b69ecc8529924848e020a586e70David Schleef		if (div2 <= 65536) {
109703afc38a0e79b69ecc8529924848e020a586e70David Schleef			*nanosec = div2 * base;
110703afc38a0e79b69ecc8529924848e020a586e70David Schleef			*d1 = div1 & 0xffff;
111703afc38a0e79b69ecc8529924848e020a586e70David Schleef			*d2 = div2 & 0xffff;
112703afc38a0e79b69ecc8529924848e020a586e70David Schleef			return;
113703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
114703afc38a0e79b69ecc8529924848e020a586e70David Schleef	}
115703afc38a0e79b69ecc8529924848e020a586e70David Schleef
116703afc38a0e79b69ecc8529924848e020a586e70David Schleef	/* shouldn't get here */
117703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1 = 0x10000;
118703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div2 = 0x10000;
119703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*nanosec = div1 * div2 * i8253_osc_base;
120703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*d1 = div1 & 0xffff;
121703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*d2 = div2 & 0xffff;
122703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
123703afc38a0e79b69ecc8529924848e020a586e70David Schleef
124703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline void i8253_cascade_ns_to_timer_2div(int i8253_osc_base,
1250a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						  unsigned int *d1,
1260a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						  unsigned int *d2,
1270a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						  unsigned int *nanosec,
1280a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral						  int round_mode)
129703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
130703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int divider;
131703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int div1, div2;
132703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int div1_glb, div2_glb, ns_glb;
133703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int div1_lub, div2_lub, ns_lub;
134703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int ns;
135703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int start;
136703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int ns_low, ns_high;
137703afc38a0e79b69ecc8529924848e020a586e70David Schleef	static const unsigned int max_count = 0x10000;
138703afc38a0e79b69ecc8529924848e020a586e70David Schleef	/* exit early if everything is already correct (this can save time
139703afc38a0e79b69ecc8529924848e020a586e70David Schleef	 * since this function may be called repeatedly during command tests
140703afc38a0e79b69ecc8529924848e020a586e70David Schleef	 * and execution) */
141703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1 = *d1 ? *d1 : max_count;
142703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div2 = *d2 ? *d2 : max_count;
143703afc38a0e79b69ecc8529924848e020a586e70David Schleef	divider = div1 * div2;
144703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (div1 * div2 * i8253_osc_base == *nanosec &&
1450a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	    div1 > 1 && div1 <= max_count && div2 > 1 && div2 <= max_count &&
1460a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	    /* check for overflow */
1470a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	    divider > div1 && divider > div2 &&
1480a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	    divider * i8253_osc_base > divider &&
1490a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	    divider * i8253_osc_base > i8253_osc_base) {
150703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return;
151703afc38a0e79b69ecc8529924848e020a586e70David Schleef	}
152703afc38a0e79b69ecc8529924848e020a586e70David Schleef
153703afc38a0e79b69ecc8529924848e020a586e70David Schleef	divider = *nanosec / i8253_osc_base;
154703afc38a0e79b69ecc8529924848e020a586e70David Schleef
155703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1_lub = div2_lub = 0;
156703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div1_glb = div2_glb = 0;
157703afc38a0e79b69ecc8529924848e020a586e70David Schleef
158703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ns_glb = 0;
159703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ns_lub = 0xffffffff;
160703afc38a0e79b69ecc8529924848e020a586e70David Schleef
161703afc38a0e79b69ecc8529924848e020a586e70David Schleef	div2 = max_count;
162703afc38a0e79b69ecc8529924848e020a586e70David Schleef	start = divider / div2;
163703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (start < 2)
164703afc38a0e79b69ecc8529924848e020a586e70David Schleef		start = 2;
165703afc38a0e79b69ecc8529924848e020a586e70David Schleef	for (div1 = start; div1 <= divider / div1 + 1 && div1 <= max_count;
1660a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	     div1++) {
167703afc38a0e79b69ecc8529924848e020a586e70David Schleef		for (div2 = divider / div1;
1680a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral		     div1 * div2 <= divider + div1 + 1 && div2 <= max_count;
1690a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral		     div2++) {
170703afc38a0e79b69ecc8529924848e020a586e70David Schleef			ns = i8253_osc_base * div1 * div2;
171703afc38a0e79b69ecc8529924848e020a586e70David Schleef			if (ns <= *nanosec && ns > ns_glb) {
172703afc38a0e79b69ecc8529924848e020a586e70David Schleef				ns_glb = ns;
173703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div1_glb = div1;
174703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div2_glb = div2;
175703afc38a0e79b69ecc8529924848e020a586e70David Schleef			}
176703afc38a0e79b69ecc8529924848e020a586e70David Schleef			if (ns >= *nanosec && ns < ns_lub) {
177703afc38a0e79b69ecc8529924848e020a586e70David Schleef				ns_lub = ns;
178703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div1_lub = div1;
179703afc38a0e79b69ecc8529924848e020a586e70David Schleef				div2_lub = div2;
180703afc38a0e79b69ecc8529924848e020a586e70David Schleef			}
181703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
182703afc38a0e79b69ecc8529924848e020a586e70David Schleef	}
183703afc38a0e79b69ecc8529924848e020a586e70David Schleef
184703afc38a0e79b69ecc8529924848e020a586e70David Schleef	round_mode &= TRIG_ROUND_MASK;
185703afc38a0e79b69ecc8529924848e020a586e70David Schleef	switch (round_mode) {
186703afc38a0e79b69ecc8529924848e020a586e70David Schleef	case TRIG_ROUND_NEAREST:
187703afc38a0e79b69ecc8529924848e020a586e70David Schleef	default:
188703afc38a0e79b69ecc8529924848e020a586e70David Schleef		ns_high = div1_lub * div2_lub * i8253_osc_base;
189703afc38a0e79b69ecc8529924848e020a586e70David Schleef		ns_low = div1_glb * div2_glb * i8253_osc_base;
190703afc38a0e79b69ecc8529924848e020a586e70David Schleef		if (ns_high - *nanosec < *nanosec - ns_low) {
191703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div1 = div1_lub;
192703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = div2_lub;
193703afc38a0e79b69ecc8529924848e020a586e70David Schleef		} else {
194703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div1 = div1_glb;
195703afc38a0e79b69ecc8529924848e020a586e70David Schleef			div2 = div2_glb;
196703afc38a0e79b69ecc8529924848e020a586e70David Schleef		}
197703afc38a0e79b69ecc8529924848e020a586e70David Schleef		break;
198703afc38a0e79b69ecc8529924848e020a586e70David Schleef	case TRIG_ROUND_UP:
199703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div1 = div1_lub;
200703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div2 = div2_lub;
201703afc38a0e79b69ecc8529924848e020a586e70David Schleef		break;
202703afc38a0e79b69ecc8529924848e020a586e70David Schleef	case TRIG_ROUND_DOWN:
203703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div1 = div1_glb;
204703afc38a0e79b69ecc8529924848e020a586e70David Schleef		div2 = div2_glb;
205703afc38a0e79b69ecc8529924848e020a586e70David Schleef		break;
206703afc38a0e79b69ecc8529924848e020a586e70David Schleef	}
207703afc38a0e79b69ecc8529924848e020a586e70David Schleef
208703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*nanosec = div1 * div2 * i8253_osc_base;
209155b44aae0ed9a0f9aecc8c528ba5bc3b26f8377Greg Kroah-Hartman	/*  masking is done since counter maps zero to 0x10000 */
210155b44aae0ed9a0f9aecc8c528ba5bc3b26f8377Greg Kroah-Hartman	*d1 = div1 & 0xffff;
211703afc38a0e79b69ecc8529924848e020a586e70David Schleef	*d2 = div2 & 0xffff;
212703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return;
213703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
214703afc38a0e79b69ecc8529924848e020a586e70David Schleef
215703afc38a0e79b69ecc8529924848e020a586e70David Schleef#ifndef CMDTEST
216703afc38a0e79b69ecc8529924848e020a586e70David Schleef/* i8254_load programs 8254 counter chip.  It should also work for the 8253.
217a88e29cfd8e59c82cf63440117bd1d61761f0f87Graham M Howe * base_address is the lowest io address
218a88e29cfd8e59c82cf63440117bd1d61761f0f87Graham M Howe * for the chip (the address of counter 0).
219703afc38a0e79b69ecc8529924848e020a586e70David Schleef * counter_number is the counter you want to load (0,1 or 2)
220703afc38a0e79b69ecc8529924848e020a586e70David Schleef * count is the number to load into the counter.
221703afc38a0e79b69ecc8529924848e020a586e70David Schleef *
222703afc38a0e79b69ecc8529924848e020a586e70David Schleef * You probably want to use mode 2.
223703afc38a0e79b69ecc8529924848e020a586e70David Schleef *
224703afc38a0e79b69ecc8529924848e020a586e70David Schleef * Use i8254_mm_load() if you board uses memory-mapped io, it is
225703afc38a0e79b69ecc8529924848e020a586e70David Schleef * the same as i8254_load() except it uses writeb() instead of outb().
226703afc38a0e79b69ecc8529924848e020a586e70David Schleef *
227703afc38a0e79b69ecc8529924848e020a586e70David Schleef * Neither i8254_load() or i8254_read() do their loading/reading
228703afc38a0e79b69ecc8529924848e020a586e70David Schleef * atomically.  The 16 bit read/writes are performed with two successive
229703afc38a0e79b69ecc8529924848e020a586e70David Schleef * 8 bit read/writes.  So if two parts of your driver do a load/read on
230703afc38a0e79b69ecc8529924848e020a586e70David Schleef * the same counter, it may be necessary to protect these functions
231703afc38a0e79b69ecc8529924848e020a586e70David Schleef * with a spinlock.
232703afc38a0e79b69ecc8529924848e020a586e70David Schleef *
233703afc38a0e79b69ecc8529924848e020a586e70David Schleef * FMH
234703afc38a0e79b69ecc8529924848e020a586e70David Schleef */
235703afc38a0e79b69ecc8529924848e020a586e70David Schleef
236703afc38a0e79b69ecc8529924848e020a586e70David Schleef#define i8254_control_reg	3
237703afc38a0e79b69ecc8529924848e020a586e70David Schleef
238703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_load(unsigned long base_address, unsigned int regshift,
2390a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			     unsigned int counter_number, unsigned int count,
2400a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			     unsigned int mode)
241703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
242703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
243703afc38a0e79b69ecc8529924848e020a586e70David Schleef
244703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
245703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
246703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (count > 0xffff)
247703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
248703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (mode > 5)
249703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
250703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if ((mode == 2 || mode == 3) && count == 1)
251703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
252703afc38a0e79b69ecc8529924848e020a586e70David Schleef
253703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
25467d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= 0x30;		/*  load low then high byte */
25567d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= (mode << 1);	/*  set counter mode */
256703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (i8254_control_reg << regshift));
25767d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = count & 0xff;	/*  lsb of counter value */
258703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (counter_number << regshift));
25967d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = (count >> 8) & 0xff;	/*  msb of counter value */
260703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (counter_number << regshift));
261703afc38a0e79b69ecc8529924848e020a586e70David Schleef
262703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return 0;
263703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
264703afc38a0e79b69ecc8529924848e020a586e70David Schleef
265703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_mm_load(void *base_address, unsigned int regshift,
2660a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				unsigned int counter_number, unsigned int count,
2670a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				unsigned int mode)
268703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
269703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
270703afc38a0e79b69ecc8529924848e020a586e70David Schleef
271703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
272703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
273703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (count > 0xffff)
274703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
275703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (mode > 5)
276703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
277703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if ((mode == 2 || mode == 3) && count == 1)
278703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
279703afc38a0e79b69ecc8529924848e020a586e70David Schleef
280703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
28167d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= 0x30;		/*  load low then high byte */
28267d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= (mode << 1);	/*  set counter mode */
283703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (i8254_control_reg << regshift));
28467d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = count & 0xff;	/*  lsb of counter value */
285703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (counter_number << regshift));
28667d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = (count >> 8) & 0xff;	/*  msb of counter value */
287703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (counter_number << regshift));
288703afc38a0e79b69ecc8529924848e020a586e70David Schleef
289703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return 0;
290703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
291703afc38a0e79b69ecc8529924848e020a586e70David Schleef
292703afc38a0e79b69ecc8529924848e020a586e70David Schleef/* Returns 16 bit counter value, should work for 8253 also.*/
293703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_read(unsigned long base_address, unsigned int regshift,
2940a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			     unsigned int counter_number)
295703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
296703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
297703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int ret;
298703afc38a0e79b69ecc8529924848e020a586e70David Schleef
299703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
300703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
301703afc38a0e79b69ecc8529924848e020a586e70David Schleef
30267d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  latch counter */
303703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
304703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (i8254_control_reg << regshift));
305703afc38a0e79b69ecc8529924848e020a586e70David Schleef
30667d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  read lsb */
307703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ret = inb(base_address + (counter_number << regshift));
30867d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  read msb */
309703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ret += inb(base_address + (counter_number << regshift)) << 8;
310703afc38a0e79b69ecc8529924848e020a586e70David Schleef
311703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return ret;
312703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
313703afc38a0e79b69ecc8529924848e020a586e70David Schleef
314703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_mm_read(void *base_address, unsigned int regshift,
3150a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				unsigned int counter_number)
316703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
317703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
318703afc38a0e79b69ecc8529924848e020a586e70David Schleef	int ret;
319703afc38a0e79b69ecc8529924848e020a586e70David Schleef
320703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
321703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
322703afc38a0e79b69ecc8529924848e020a586e70David Schleef
32367d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  latch counter */
324703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
325703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (i8254_control_reg << regshift));
326703afc38a0e79b69ecc8529924848e020a586e70David Schleef
32767d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  read lsb */
328703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ret = readb(base_address + (counter_number << regshift));
32967d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	/*  read msb */
330703afc38a0e79b69ecc8529924848e020a586e70David Schleef	ret += readb(base_address + (counter_number << regshift)) << 8;
331703afc38a0e79b69ecc8529924848e020a586e70David Schleef
332703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return ret;
333703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
334703afc38a0e79b69ecc8529924848e020a586e70David Schleef
335703afc38a0e79b69ecc8529924848e020a586e70David Schleef/* Loads 16 bit initial counter value, should work for 8253 also. */
336703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline void i8254_write(unsigned long base_address,
3370a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       unsigned int regshift,
3380a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       unsigned int counter_number, unsigned int count)
339703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
340703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
341703afc38a0e79b69ecc8529924848e020a586e70David Schleef
342703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
343703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return;
344703afc38a0e79b69ecc8529924848e020a586e70David Schleef
34567d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = count & 0xff;	/*  lsb of counter value */
346703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (counter_number << regshift));
34767d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = (count >> 8) & 0xff;	/*  msb of counter value */
348703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (counter_number << regshift));
349703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
350703afc38a0e79b69ecc8529924848e020a586e70David Schleef
351703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline void i8254_mm_write(void *base_address,
3520a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				  unsigned int regshift,
3530a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				  unsigned int counter_number,
3540a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				  unsigned int count)
355703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
356703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
357703afc38a0e79b69ecc8529924848e020a586e70David Schleef
358703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
359703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return;
360703afc38a0e79b69ecc8529924848e020a586e70David Schleef
36167d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = count & 0xff;	/*  lsb of counter value */
362703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (counter_number << regshift));
36367d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte = (count >> 8) & 0xff;	/*  msb of counter value */
364703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (counter_number << regshift));
365703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
366703afc38a0e79b69ecc8529924848e020a586e70David Schleef
367703afc38a0e79b69ecc8529924848e020a586e70David Schleef/* Set counter mode, should work for 8253 also.
368703afc38a0e79b69ecc8529924848e020a586e70David Schleef * Note: the 'mode' value is different to that for i8254_load() and comes
369703afc38a0e79b69ecc8529924848e020a586e70David Schleef * from the INSN_CONFIG_8254_SET_MODE command:
370703afc38a0e79b69ecc8529924848e020a586e70David Schleef *   I8254_MODE0, I8254_MODE1, ..., I8254_MODE5
371703afc38a0e79b69ecc8529924848e020a586e70David Schleef * OR'ed with:
372703afc38a0e79b69ecc8529924848e020a586e70David Schleef *   I8254_BCD, I8254_BINARY
373703afc38a0e79b69ecc8529924848e020a586e70David Schleef */
374703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_set_mode(unsigned long base_address,
3750a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				 unsigned int regshift,
3760a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				 unsigned int counter_number, unsigned int mode)
377703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
378703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
379703afc38a0e79b69ecc8529924848e020a586e70David Schleef
380703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
381703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
382703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (mode > (I8254_MODE5 | I8254_BINARY))
383703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
384703afc38a0e79b69ecc8529924848e020a586e70David Schleef
385703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
38667d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= 0x30;		/*  load low then high byte */
38767d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= mode;		/*  set counter mode and BCD|binary */
388703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(byte, base_address + (i8254_control_reg << regshift));
389703afc38a0e79b69ecc8529924848e020a586e70David Schleef
390703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return 0;
391703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
392703afc38a0e79b69ecc8529924848e020a586e70David Schleef
393703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_mm_set_mode(void *base_address,
3940a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				    unsigned int regshift,
3950a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				    unsigned int counter_number,
3960a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				    unsigned int mode)
397703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
398703afc38a0e79b69ecc8529924848e020a586e70David Schleef	unsigned int byte;
399703afc38a0e79b69ecc8529924848e020a586e70David Schleef
400703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (counter_number > 2)
401703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
402703afc38a0e79b69ecc8529924848e020a586e70David Schleef	if (mode > (I8254_MODE5 | I8254_BINARY))
403703afc38a0e79b69ecc8529924848e020a586e70David Schleef		return -1;
404703afc38a0e79b69ecc8529924848e020a586e70David Schleef
405703afc38a0e79b69ecc8529924848e020a586e70David Schleef	byte = counter_number << 6;
40667d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= 0x30;		/*  load low then high byte */
40767d83b4fb96ab304a47ba5af9a99818b38c9fd3eBill Pemberton	byte |= mode;		/*  set counter mode and BCD|binary */
408703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(byte, base_address + (i8254_control_reg << regshift));
409703afc38a0e79b69ecc8529924848e020a586e70David Schleef
410703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return 0;
411703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
412703afc38a0e79b69ecc8529924848e020a586e70David Schleef
413703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_status(unsigned long base_address,
4140a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       unsigned int regshift,
4150a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral			       unsigned int counter_number)
416703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
417703afc38a0e79b69ecc8529924848e020a586e70David Schleef	outb(0xE0 | (2 << counter_number),
4180a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	     base_address + (i8254_control_reg << regshift));
419703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return inb(base_address + (counter_number << regshift));
420703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
421703afc38a0e79b69ecc8529924848e020a586e70David Schleef
422703afc38a0e79b69ecc8529924848e020a586e70David Schleefstatic inline int i8254_mm_status(void *base_address,
4230a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				  unsigned int regshift,
4240a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral				  unsigned int counter_number)
425703afc38a0e79b69ecc8529924848e020a586e70David Schleef{
426703afc38a0e79b69ecc8529924848e020a586e70David Schleef	writeb(0xE0 | (2 << counter_number),
4270a85b6f0ab0d2edb0d41b32697111ce0e4f43496Mithlesh Thukral	       base_address + (i8254_control_reg << regshift));
428703afc38a0e79b69ecc8529924848e020a586e70David Schleef	return readb(base_address + (counter_number << regshift));
429703afc38a0e79b69ecc8529924848e020a586e70David Schleef}
430703afc38a0e79b69ecc8529924848e020a586e70David Schleef
431703afc38a0e79b69ecc8529924848e020a586e70David Schleef#endif
432703afc38a0e79b69ecc8529924848e020a586e70David Schleef
433703afc38a0e79b69ecc8529924848e020a586e70David Schleef#endif
434