x86emu_i8254.c revision 1.1.28.1 1 1.1.28.1 yamt /* $NetBSD: x86emu_i8254.c,v 1.1.28.1 2014/05/22 11:26:31 yamt Exp $ */
2 1.1 joerg
3 1.1 joerg /*-
4 1.1 joerg * Copyright (c) 2007 Joerg Sonnenberger <joerg (at) NetBSD.org>.
5 1.1 joerg * All rights reserved.
6 1.1 joerg *
7 1.1 joerg * Redistribution and use in source and binary forms, with or without
8 1.1 joerg * modification, are permitted provided that the following conditions
9 1.1 joerg * are met:
10 1.1 joerg *
11 1.1 joerg * 1. Redistributions of source code must retain the above copyright
12 1.1 joerg * notice, this list of conditions and the following disclaimer.
13 1.1 joerg * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 joerg * notice, this list of conditions and the following disclaimer in
15 1.1 joerg * the documentation and/or other materials provided with the
16 1.1 joerg * distribution.
17 1.1 joerg *
18 1.1 joerg * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 1.1 joerg * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 1.1 joerg * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 1.1 joerg * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 1.1 joerg * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 1.1 joerg * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 1.1 joerg * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 1.1 joerg * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 1.1 joerg * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 1.1 joerg * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28 1.1 joerg * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 1.1 joerg * SUCH DAMAGE.
30 1.1 joerg */
31 1.1 joerg
32 1.1 joerg #include <x86emu/x86emu_i8254.h>
33 1.1 joerg
34 1.1 joerg #ifndef _KERNEL
35 1.1 joerg #include <assert.h>
36 1.1 joerg #define KASSERT(x) assert(x)
37 1.1 joerg #endif
38 1.1 joerg
39 1.1 joerg #define I8254_FREQ 1193182 /* Hz */
40 1.1 joerg
41 1.1 joerg static uint16_t
42 1.1 joerg bcd2bin(uint16_t bcd_val)
43 1.1 joerg {
44 1.1 joerg return bcd_val % 0x10 + (bcd_val / 0x10 % 0x10 * 10) +
45 1.1 joerg (bcd_val / 0x100 % 0x10 * 100) + (bcd_val / 0x1000 % 0x10 * 1000);
46 1.1 joerg }
47 1.1 joerg
48 1.1 joerg static uint16_t
49 1.1 joerg bin2bcd(uint16_t bin_val)
50 1.1 joerg {
51 1.1 joerg return (bin_val % 10) + (bin_val / 10 % 10 * 0x10) +
52 1.1 joerg (bin_val / 100 % 10 * 0x100) + (bin_val / 1000 % 10 * 0x1000);
53 1.1 joerg }
54 1.1 joerg
55 1.1 joerg /*
56 1.1 joerg * Compute tick of the virtual timer based on start time and
57 1.1 joerg * current time.
58 1.1 joerg */
59 1.1 joerg static uint64_t
60 1.1 joerg x86emu_i8254_gettick(struct x86emu_i8254 *sc)
61 1.1 joerg {
62 1.1 joerg struct timespec curtime;
63 1.1 joerg uint64_t tick;
64 1.1 joerg
65 1.1 joerg (*sc->gettime)(&curtime);
66 1.1 joerg
67 1.1 joerg tick = (curtime.tv_sec - sc->base_time.tv_sec) * I8254_FREQ;
68 1.1 joerg tick += (uint64_t)(curtime.tv_nsec - sc->base_time.tv_nsec) * I8254_FREQ / 1000000000;
69 1.1 joerg
70 1.1 joerg return tick;
71 1.1 joerg }
72 1.1 joerg
73 1.1 joerg /* Compute current counter value. */
74 1.1 joerg static uint16_t
75 1.1 joerg x86emu_i8254_counter(struct x86emu_i8254_timer *timer, uint64_t curtick)
76 1.1 joerg {
77 1.1 joerg uint16_t maxtick;
78 1.1 joerg
79 1.1 joerg /* Initial value if timer is disabled or not yet started */
80 1.1 joerg if (timer->gate_high || timer->start_tick > curtick)
81 1.1 joerg return timer->active_counter;
82 1.1 joerg
83 1.1 joerg /* Compute maximum value based on BCD/binary mode */
84 1.1 joerg if (timer->active_is_bcd)
85 1.1 joerg maxtick = 9999;
86 1.1 joerg else
87 1.1 joerg maxtick = 0xffff;
88 1.1 joerg
89 1.1 joerg curtick -= timer->start_tick;
90 1.1 joerg
91 1.1 joerg /* Check if first run over the time counter is over. */
92 1.1 joerg if (curtick <= timer->active_counter)
93 1.1 joerg return timer->active_counter - curtick;
94 1.1 joerg /* Now curtick > 0 as both values above are unsigned. */
95 1.1 joerg
96 1.1 joerg /* Special case of active_counter == maxtick + 1 */
97 1.1 joerg if (timer->active_counter == 0 && curtick - 1 <= maxtick)
98 1.1 joerg return maxtick + 1 - curtick;
99 1.1 joerg
100 1.1 joerg /* For periodic timers, compute current periode. */
101 1.1 joerg if (timer->active_mode & 2)
102 1.1 joerg return timer->active_counter - curtick % timer->active_counter;
103 1.1 joerg
104 1.1 joerg /* For one-shot timers, compute overflow. */
105 1.1 joerg curtick -= maxtick + 1;
106 1.1 joerg return maxtick - curtick % maxtick + 1;
107 1.1 joerg }
108 1.1 joerg
109 1.1 joerg static bool
110 1.1 joerg x86emu_i8254_out(struct x86emu_i8254_timer *timer, uint64_t curtick)
111 1.1 joerg {
112 1.1 joerg /*
113 1.1 joerg * TODO:
114 1.1 joerg * Mode 0:
115 1.1 joerg * After the write of the LSB and before the write of the MSB,
116 1.1 joerg * this should return LOW.
117 1.1 joerg */
118 1.1 joerg
119 1.1 joerg /*
120 1.1 joerg * If the timer was not started yet or is disabled,
121 1.1 joerg * only Mode 0 is LOW
122 1.1 joerg */
123 1.1 joerg if (timer->gate_high || timer->start_tick > curtick)
124 1.1 joerg return (timer->active_mode != 0);
125 1.1 joerg
126 1.1 joerg curtick -= timer->start_tick;
127 1.1 joerg
128 1.1 joerg /* Return LOW until counter is 0, afterwards HIGH until reload. */
129 1.1 joerg if (timer->active_mode == 0 || timer->active_mode == 1)
130 1.1 joerg return curtick >= timer->start_tick;
131 1.1 joerg
132 1.1 joerg /* Return LOW until the counter is 0, raise to HIGH and go LOW again. */
133 1.1 joerg if (timer->active_mode == 5 || timer->active_mode == 7)
134 1.1 joerg return curtick != timer->start_tick;
135 1.1 joerg
136 1.1 joerg /*
137 1.1 joerg * Return LOW until the counter is 1, raise to HIGH and go LOW
138 1.1 joerg * again. Afterwards reload the counter.
139 1.1 joerg */
140 1.1 joerg if (timer->active_mode == 2 || timer->active_mode == 3) {
141 1.1 joerg curtick %= timer->active_counter;
142 1.1 joerg return curtick + 1 != timer->active_counter;
143 1.1 joerg }
144 1.1 joerg
145 1.1 joerg /*
146 1.1 joerg * If the initial counter is even, return HIGH for the first half
147 1.1 joerg * and LOW for the second. If it is even, bias the first half.
148 1.1 joerg */
149 1.1 joerg curtick %= timer->active_counter;
150 1.1 joerg return curtick < (timer->active_counter + 1) / 2;
151 1.1 joerg }
152 1.1 joerg
153 1.1 joerg static void
154 1.1 joerg x86emu_i8254_latch_status(struct x86emu_i8254_timer *timer, uint64_t curtick)
155 1.1 joerg {
156 1.1 joerg if (timer->status_is_latched)
157 1.1 joerg return;
158 1.1 joerg timer->latched_status = timer->active_is_bcd ? 1 : 0;
159 1.1 joerg timer->latched_status |= timer->active_mode << 1;
160 1.1 joerg timer->latched_status |= timer->rw_status;
161 1.1 joerg timer->latched_status |= timer->null_count ? 0x40 : 0;
162 1.1 joerg }
163 1.1 joerg
164 1.1 joerg static void
165 1.1 joerg x86emu_i8254_latch_counter(struct x86emu_i8254_timer *timer, uint64_t curtick)
166 1.1 joerg {
167 1.1 joerg if (!timer->counter_is_latched)
168 1.1 joerg return; /* Already latched. */
169 1.1 joerg timer->latched_counter = x86emu_i8254_counter(timer, curtick);
170 1.1 joerg timer->counter_is_latched = true;
171 1.1 joerg }
172 1.1 joerg
173 1.1 joerg static void
174 1.1 joerg x86emu_i8254_write_command(struct x86emu_i8254 *sc, uint8_t val)
175 1.1 joerg {
176 1.1 joerg struct x86emu_i8254_timer *timer;
177 1.1 joerg int i;
178 1.1 joerg
179 1.1 joerg if ((val >> 6) == 3) {
180 1.1 joerg /* Read Back Command */
181 1.1 joerg uint64_t curtick;
182 1.1 joerg
183 1.1 joerg curtick = x86emu_i8254_gettick(sc);
184 1.1 joerg for (i = 0; i < 3; ++i) {
185 1.1 joerg timer = &sc->timer[i];
186 1.1 joerg
187 1.1 joerg if ((val & (2 << i)) == 0)
188 1.1 joerg continue;
189 1.1 joerg if ((val & 0x10) != 0)
190 1.1 joerg x86emu_i8254_latch_status(timer, curtick);
191 1.1 joerg if ((val & 0x20) != 0)
192 1.1 joerg x86emu_i8254_latch_counter(timer, curtick);
193 1.1 joerg }
194 1.1 joerg return;
195 1.1 joerg }
196 1.1 joerg
197 1.1 joerg timer = &sc->timer[val >> 6];
198 1.1 joerg
199 1.1 joerg switch (val & 0x30) {
200 1.1 joerg case 0:
201 1.1 joerg x86emu_i8254_latch_counter(timer, x86emu_i8254_gettick(sc));
202 1.1 joerg return;
203 1.1 joerg case 1:
204 1.1 joerg timer->write_lsb = timer->read_lsb = true;
205 1.1 joerg timer->write_msb = timer->read_msb = false;
206 1.1 joerg break;
207 1.1 joerg case 2:
208 1.1 joerg timer->write_lsb = timer->read_lsb = false;
209 1.1 joerg timer->write_msb = timer->read_msb = true;
210 1.1 joerg break;
211 1.1 joerg case 3:
212 1.1 joerg timer->write_lsb = timer->read_lsb = true;
213 1.1 joerg timer->write_msb = timer->read_msb = true;
214 1.1 joerg break;
215 1.1 joerg }
216 1.1 joerg timer->rw_status = val & 0x30;
217 1.1 joerg timer->null_count = true;
218 1.1 joerg timer->new_mode = (val >> 1) & 0x7;
219 1.1 joerg timer->new_is_bcd = (val & 1) == 1;
220 1.1 joerg }
221 1.1 joerg
222 1.1 joerg static uint8_t
223 1.1 joerg x86emu_i8254_read_counter(struct x86emu_i8254 *sc,
224 1.1 joerg struct x86emu_i8254_timer *timer)
225 1.1 joerg {
226 1.1 joerg uint16_t val;
227 1.1 joerg uint8_t output;
228 1.1 joerg
229 1.1 joerg /* If status was latched by Read Back Command, return it. */
230 1.1 joerg if (timer->status_is_latched) {
231 1.1 joerg timer->status_is_latched = false;
232 1.1 joerg return timer->latched_status;
233 1.1 joerg }
234 1.1 joerg
235 1.1 joerg /*
236 1.1 joerg * The value of the counter is either the latched value
237 1.1 joerg * or the current counter.
238 1.1 joerg */
239 1.1 joerg if (timer->counter_is_latched)
240 1.1 joerg val = timer->latched_counter;
241 1.1 joerg else
242 1.1 joerg val = x86emu_i8254_counter(&sc->timer[2],
243 1.1 joerg x86emu_i8254_gettick(sc));
244 1.1 joerg
245 1.1 joerg if (timer->active_is_bcd)
246 1.1 joerg val = bin2bcd(val);
247 1.1 joerg
248 1.1 joerg /* Extract requested byte. */
249 1.1 joerg if (timer->read_lsb) {
250 1.1 joerg output = val & 0xff;
251 1.1 joerg timer->read_lsb = false;
252 1.1 joerg } else if (timer->read_msb) {
253 1.1 joerg output = val >> 8;
254 1.1 joerg timer->read_msb = false;
255 1.1 joerg } else
256 1.1 joerg output = 0; /* Undefined value. */
257 1.1 joerg
258 1.1 joerg /* Clean latched status if all requested bytes have been read. */
259 1.1 joerg if (!timer->read_lsb && !timer->read_msb)
260 1.1 joerg timer->counter_is_latched = false;
261 1.1 joerg
262 1.1 joerg return output;
263 1.1 joerg }
264 1.1 joerg
265 1.1 joerg static void
266 1.1 joerg x86emu_i8254_write_counter(struct x86emu_i8254 *sc,
267 1.1 joerg struct x86emu_i8254_timer *timer, uint8_t val)
268 1.1 joerg {
269 1.1 joerg /* Nothing to write, undefined. */
270 1.1 joerg if (!timer->write_lsb && !timer->write_msb)
271 1.1 joerg return;
272 1.1 joerg
273 1.1 joerg /* Update requested bytes. */
274 1.1 joerg if (timer->write_lsb) {
275 1.1 joerg timer->new_counter &= ~0xff;
276 1.1 joerg timer->new_counter |= val;
277 1.1 joerg timer->write_lsb = false;
278 1.1 joerg } else {
279 1.1 joerg KASSERT(timer->write_msb);
280 1.1 joerg timer->new_counter &= ~0xff00;
281 1.1 joerg timer->new_counter |= val << 8;
282 1.1 joerg timer->write_msb = false;
283 1.1 joerg }
284 1.1 joerg
285 1.1 joerg /* If all requested bytes have been written, update counter. */
286 1.1 joerg if (!timer->write_lsb && !timer->write_msb) {
287 1.1 joerg timer->null_count = false;
288 1.1 joerg timer->counter_is_latched = false;
289 1.1 joerg timer->status_is_latched = false;
290 1.1 joerg timer->active_is_bcd = timer->new_is_bcd;
291 1.1 joerg timer->active_mode = timer->new_mode;
292 1.1 joerg timer->start_tick = x86emu_i8254_gettick(sc) + 1;
293 1.1 joerg if (timer->new_is_bcd)
294 1.1 joerg timer->active_counter = bcd2bin(timer->new_counter);
295 1.1 joerg }
296 1.1 joerg }
297 1.1 joerg
298 1.1 joerg static uint8_t
299 1.1 joerg x86emu_i8254_read_nmi(struct x86emu_i8254 *sc)
300 1.1 joerg {
301 1.1 joerg uint8_t val;
302 1.1 joerg
303 1.1 joerg val = (sc->timer[2].gate_high) ? 1 : 0;
304 1.1 joerg if (x86emu_i8254_out(&sc->timer[2], x86emu_i8254_gettick(sc)))
305 1.1 joerg val |= 0x20;
306 1.1 joerg
307 1.1 joerg return val;
308 1.1 joerg }
309 1.1 joerg
310 1.1 joerg static void
311 1.1 joerg x86emu_i8254_write_nmi(struct x86emu_i8254 *sc, uint8_t val)
312 1.1 joerg {
313 1.1 joerg bool old_gate;
314 1.1 joerg
315 1.1 joerg old_gate = sc->timer[2].gate_high;
316 1.1 joerg sc->timer[2].gate_high = (val & 1) == 1;
317 1.1 joerg if (!old_gate && sc->timer[2].gate_high)
318 1.1 joerg sc->timer[2].start_tick = x86emu_i8254_gettick(sc) + 1;
319 1.1 joerg }
320 1.1 joerg
321 1.1 joerg void
322 1.1 joerg x86emu_i8254_init(struct x86emu_i8254 *sc, void (*gettime)(struct timespec *))
323 1.1 joerg {
324 1.1 joerg struct x86emu_i8254_timer *timer;
325 1.1 joerg int i;
326 1.1 joerg
327 1.1 joerg sc->gettime = gettime;
328 1.1 joerg (*sc->gettime)(&sc->base_time);
329 1.1 joerg
330 1.1 joerg for (i = 0; i < 3; ++i) {
331 1.1 joerg timer = &sc->timer[i];
332 1.1 joerg timer->gate_high = false;
333 1.1 joerg timer->start_tick = 0;
334 1.1 joerg timer->active_counter = 0;
335 1.1 joerg timer->active_mode = 0;
336 1.1 joerg timer->active_is_bcd = false;
337 1.1 joerg timer->counter_is_latched = false;
338 1.1 joerg timer->read_lsb = false;
339 1.1 joerg timer->read_msb = false;
340 1.1 joerg timer->status_is_latched = false;
341 1.1 joerg timer->null_count = false;
342 1.1 joerg }
343 1.1 joerg }
344 1.1 joerg
345 1.1 joerg uint8_t
346 1.1 joerg x86emu_i8254_inb(struct x86emu_i8254 *sc, uint16_t port)
347 1.1 joerg {
348 1.1 joerg KASSERT(x86emu_i8254_claim_port(sc, port));
349 1.1 joerg if (port == 0x40)
350 1.1 joerg return x86emu_i8254_read_counter(sc, &sc->timer[0]);
351 1.1 joerg if (port == 0x41)
352 1.1 joerg return x86emu_i8254_read_counter(sc, &sc->timer[1]);
353 1.1 joerg if (port == 0x42)
354 1.1 joerg return x86emu_i8254_read_counter(sc, &sc->timer[2]);
355 1.1 joerg if (port == 0x43)
356 1.1 joerg return 0xff; /* unsupported */
357 1.1 joerg return x86emu_i8254_read_nmi(sc);
358 1.1 joerg }
359 1.1 joerg
360 1.1 joerg void
361 1.1 joerg x86emu_i8254_outb(struct x86emu_i8254 *sc, uint16_t port, uint8_t val)
362 1.1 joerg {
363 1.1 joerg KASSERT(x86emu_i8254_claim_port(sc, port));
364 1.1 joerg if (port == 0x40)
365 1.1 joerg x86emu_i8254_write_counter(sc, &sc->timer[0], val);
366 1.1 joerg else if (port == 0x41)
367 1.1 joerg x86emu_i8254_write_counter(sc, &sc->timer[1], val);
368 1.1 joerg else if (port == 0x42)
369 1.1 joerg x86emu_i8254_write_counter(sc, &sc->timer[2], val);
370 1.1 joerg else if (port == 0x43)
371 1.1 joerg x86emu_i8254_write_command(sc, val);
372 1.1 joerg else
373 1.1 joerg x86emu_i8254_write_nmi(sc, val);
374 1.1 joerg }
375 1.1 joerg
376 1.1 joerg /* ARGSUSED */
377 1.1 joerg bool
378 1.1 joerg x86emu_i8254_claim_port(struct x86emu_i8254 *sc, uint16_t port)
379 1.1 joerg {
380 1.1 joerg /* i8254 registers */
381 1.1 joerg if (port >= 0x40 && port < 0x44)
382 1.1 joerg return true;
383 1.1 joerg /* NMI register, used to control timer 2 and the output of it */
384 1.1 joerg if (port == 0x61)
385 1.1 joerg return true;
386 1.1 joerg return false;
387 1.1 joerg }
388