]> git.buserror.net Git - polintos/scott/priv.git/blob - kernel/io/timer/i8254.cc
9dd04b3bb94b24b4f1c81f9101ece27547a93200
[polintos/scott/priv.git] / kernel / io / timer / i8254.cc
1 // io/timer/i8254.cc -- Intel 8254 and compatible timer driver
2 //
3 // This software is copyright (c) 2006 Scott Wood <scott@buserror.net>.
4 // 
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 conditions:
11 // 
12 //     * Redistributions of source code must retain the above copyright notice,
13 //       this list of conditions and the following disclaimers.
14 // 
15 //     * Redistributions in binary form must reproduce the above copyright notice,
16 //       this list of conditions and the following disclaimers in the
17 //       documentation and/or other materials provided with the distribution.
18 // 
19 //     * The names of the Software's authors and/or contributors
20 //       may not be used to endorse or promote products derived from
21 //       this Software without specific prior written permission.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
25 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
26 // CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
29 // SOFTWARE.
30
31 #include <kern/time.h>
32 #include <kern/io.h>
33 #include <lowlevel/clock.h>
34
35 namespace IO {
36 namespace Timer {
37         using Time::HWTimer;
38         using Time::monotonic_clock;
39         using Time::hw_timer;
40         using Time::Time;
41
42         class I8254 : public HWTimer {
43                 Lock::SpinLock lock;
44                 bool armed;
45         
46                 u16 read_time()
47                 {
48                         ll_out_8(0x43, 0);
49                         u16 ret = ll_in_8(0x40);
50                         ret |= (u16)ll_in_8(0x40) << 8;
51                         return ret;
52                 }
53                 
54                 void wait_for_zero()
55                 {
56                         u16 val;
57                 
58                         do {
59                                 val = read_time();
60                         } while (val < 0x8000);
61
62                         do {
63                                 val = read_time();
64                         } while (val > 0x8000);
65                 }
66                 
67                 enum {
68                         clock_freq = 1193182
69                 };
70                 
71         public:
72                 I8254()
73                 {
74                         hw_timer = this;
75                         armed = false;
76                 }
77         
78                 void calibrate()
79                 {
80                         wait_for_zero();
81                         s64 start_tick = ll_getclock();
82                         wait_for_zero();
83                         wait_for_zero();
84                         s64 end_tick = ll_getclock();
85
86                         s64 ticks_per_second = (end_tick - start_tick) * 
87                                                (u64)clock_freq / 0x20000;
88                         
89                         monotonic_clock.calibrate(ticks_per_second);
90                 }
91         
92                 void arm(Time new_expiry)
93                 {
94                         // Interrupts should always be disabled when
95                         // this is called.
96                         
97                         Lock::AutoSpinLock autolock(lock);
98
99                         // The 8254 is often slow to program, so don't re-program if the
100                         // expiry is the same.  To accomodate this, the tick timer must
101                         // be set to go off often enough that the expiry is never more
102                         // than 0xffff 8254 ticks (about 1/18.2 sec) in the future.
103
104                         if (armed && expiry != new_expiry) {
105                                 Time now;
106                                 monotonic_clock.get_time(&now);
107                                 Time rel_expiry = new_expiry - now;
108                                 expiry = new_expiry;
109                                 
110                                 u32 ticks;
111                                 
112                                 assert(rel_expiry.seconds <= 0);
113         
114                                 if (rel_expiry.seconds < 0)
115                                         ticks = 1;
116                                 else {
117                                         // Add one extra tick to make sure we round up rather than
118                                         // down; otherwise, we'll just end up programming the timer
119                                         // for one tick and trying again.
120                                         
121                                         ticks = rel_expiry.nanos * (u64)clock_freq /
122                                                 1000000000ULL + 1;
123                                         
124                                         assert(ticks <= 0xffff);
125         
126                                         if (ticks == 0)
127                                                 ticks = 1;
128                                 }
129                                 
130                                 if (!armed)
131                                         ll_out_8(0x43, 0x30);
132                                         
133                                 ll_out_8(0x40, ticks & 0xff);
134                                 ll_out_8(0x40, ticks >> 8);
135                                 armed = true;
136                         }
137                 }
138                 
139                 void disarm()
140                 {
141                         armed = false;
142                 }
143         };
144         
145         I8254 i8254;
146 }
147 }