1 // io/timer/i8254.cc -- Intel 8254 and compatible timer driver
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
5 // Permission is hereby granted, free of charge, to any person obtaining a copy of
6 // this software and associated documentation files (the "Software"), to deal with
7 // the Software without restriction, including without limitation the rights to
8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9 // of the Software, and to permit persons to whom the Software is furnished to do
10 // so, subject to the following condition:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
23 #include <kern/time.h>
25 #include <lowlevel/clock.h>
30 using Time::monotonic_clock;
34 class I8254 : public HWTimer {
41 u16 ret = ll_in_8(0x40);
42 ret |= (u16)ll_in_8(0x40) << 8;
52 } while (val < 0x8000);
56 } while (val > 0x8000);
73 s64 start_tick = ll_getclock();
76 s64 end_tick = ll_getclock();
78 s64 ticks_per_second = (end_tick - start_tick) *
79 (u64)clock_freq / 0x20000;
81 monotonic_clock.calibrate(ticks_per_second);
84 void arm(Time new_expiry)
86 // Interrupts should always be disabled when
89 Lock::AutoSpinLock autolock(lock);
91 // The 8254 is often slow to program, so don't re-program if the
92 // expiry is the same. To accomodate this, the tick timer must
93 // be set to go off often enough that the expiry is never more
94 // than 0xffff 8254 ticks (about 1/18.2 sec) in the future.
96 if (armed && expiry != new_expiry) {
98 monotonic_clock.get_time(&now);
99 Time rel_expiry = new_expiry - now;
104 assert(rel_expiry.seconds <= 0);
106 if (rel_expiry.seconds < 0)
109 // Add one extra tick to make sure we round up rather than
110 // down; otherwise, we'll just end up programming the timer
111 // for one tick and trying again.
113 ticks = rel_expiry.nanos * (u64)clock_freq /
116 assert(ticks <= 0xffff);
123 ll_out_8(0x43, 0x30);
125 ll_out_8(0x40, ticks & 0xff);
126 ll_out_8(0x40, ticks >> 8);