1 /*
2 * (c) 2008-2009 Torsten Frenzel <frenzel@os.inf.tu-dresden.de>
3 * economic rights: Technische Universität Dresden (Germany)
4 * This file is part of TUD:OS and distributed under the terms of the
5 * GNU Lesser General Public License 2.1.
6 * Please see the COPYING-LGPL-2.1 file for details.
7 */
8
9 #include <l4/sys/ipc.h>
10 #include <l4/sys/syscalls.h>
11 #include <l4/sys/kdebug.h>
12 #include <l4/util/port_io.h>
13 #include <l4/util/irq.h>
14 #include <l4/util/apic.h>
15
16
17 unsigned long apic_map_base = 0;
18 unsigned long apic_timer_divisor = 1;
19 unsigned long l4_scaler_apic_to_ms = 0;
20
21 void
apic_show_registers(void)22 apic_show_registers(void)
23 {
24 static void
25 apic_show_register_block(unsigned int beg, unsigned int len)
26 {
27 unsigned int i;
28 outhex16(beg);
29 outchar(':');
30 for (i=beg; i<beg+len; i+=0x10)
31 {
32 outchar(' ');
33 outhex32(apic_read(i));
34 }
35 outstring("\r\n");
36 }
37
38 if (!apic_map_base)
39 return;
40
41 apic_show_register_block( 0x00, 0x80); // ID, Version
42 apic_show_register_block( 0x80, 0x80); // TaskPrio, Arb, ProcPrio, DFR
43 apic_show_register_block(0x100, 0x80); // ISR 0-255
44 apic_show_register_block(0x180, 0x80); // TMR 0-255
45 apic_show_register_block(0x200, 0x80); // IRR 0-255
46 apic_show_register_block(0x300, 0x80); // ICR
47 apic_show_register_block(0x380, 0x10); // Initial Count Register
48 }
49
50 void
apic_timer_set_divisor(int newdiv)51 apic_timer_set_divisor(int newdiv)
52 {
53 int i;
54 int div = -1;
55 int divval = newdiv;
56 unsigned long tmp_value;
57
58 static int divisor_tab[8] =
59 {
60 APIC_TDR_DIV_1, APIC_TDR_DIV_2, APIC_TDR_DIV_4, APIC_TDR_DIV_8,
61 APIC_TDR_DIV_16, APIC_TDR_DIV_32, APIC_TDR_DIV_64, APIC_TDR_DIV_128
62 };
63
64 if (!apic_map_base)
65 return;
66
67 for (i=0; i<8; i++)
68 {
69 if (divval & 1)
70 {
71 if (divval & ~1)
72 {
73 enter_kdebug("bad APIC divisor");
74 }
75 div = divisor_tab[i];
76 break;
77 }
78 divval >>= 1;
79 }
80
81 if (div != -1)
82 {
83 apic_timer_divisor = newdiv;
84 tmp_value = apic_read(APIC_TDCR);
85 tmp_value &= ~0x1F;
86 tmp_value |= div;
87 apic_write(APIC_TDCR, tmp_value);
88 }
89 }
90
91
92 int
apic_check_working(void)93 apic_check_working(void)
94 {
95 #define CLOCK_TICK_RATE 1193180 /* i8254 ticks per second */
96 unsigned long count;
97 unsigned long tt1, tt2;
98
99 unsigned int calibrate_latch = (CLOCK_TICK_RATE / 20); /* 50 ms */
100
101 if (!apic_map_base)
102 return 0;
103
104 apic_timer_disable_irq();
105 apic_timer_set_divisor(1);
106 apic_timer_write(1000000000);
107
108 /* Set the Gate high, disable speaker */
109 l4util_out8((l4util_in8(0x61) & ~0x02) | 0x01, 0x61);
110
111 l4util_out8(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
112 l4util_out8(calibrate_latch & 0xff, 0x42); /* LSB of count */
113 l4util_out8(calibrate_latch >> 8, 0x42); /* MSB of count */
114
115 tt1=apic_timer_read();
116 count = 0;
117 do
118 {
119 count++;
120 } while ((l4util_in8(0x61) & 0x20) == 0);
121
122 tt2=apic_timer_read();
123 return (tt1-tt2) != 0;
124 }
125
126
127 /* activate APIC after activating by MSR was successful *
128 * see "Intel Architecture Software Developer's Manual, *
129 * Volume 3: System Programming Guide, Appendix E" */
130 void
apic_activate_by_io(void)131 apic_activate_by_io(void)
132 {
133 char old_21, old_A1;
134 unsigned long tmp_val;
135 l4_uint32_t flags;
136
137 /* mask 8259 interrupts */
138 old_21 = l4util_in8(0x21);
139 l4util_out8(0xff, 0x21);
140 old_A1 = l4util_in8(0xA1);
141 l4util_out8(0xff, 0xA1);
142
143 l4util_flags_save(&flags);
144 l4util_cli();
145
146 apic_soft_enable();
147
148 /* set LINT0 to ExtINT, edge triggered */
149 tmp_val = apic_read(APIC_LVT0);
150 tmp_val &= 0xfffe58ff;
151 tmp_val |= 0x00000700;
152 apic_write(APIC_LVT0, tmp_val);
153
154 /* set LINT1 to NMI, edge triggered */
155 tmp_val = apic_read(APIC_LVT1);
156 tmp_val &= 0xfffe58ff;
157 tmp_val |= 0x00000400;
158 apic_write(APIC_LVT1, tmp_val);
159
160 /* unmask 8259 interrupts */
161 l4util_flags_restore(&flags);
162 l4util_out8(old_A1, 0xA1);
163 l4util_out8(old_21, 0x21);
164 }
165
166 /*
167 * Return APIC clocks per ms
168 */
169 unsigned long
l4_calibrate_apic(void)170 l4_calibrate_apic (void)
171 {
172 unsigned int calibrate_latch = (CLOCK_TICK_RATE / 20); /* 50 ms */
173 unsigned int calibrate_time = 50; /* 50 ms */
174
175 if (!apic_map_base)
176 return 0;
177
178 apic_timer_disable_irq();
179 apic_timer_set_divisor(apic_timer_divisor);
180 apic_timer_write(1000000000);
181
182 /* Set the Gate high, disable speaker */
183 l4util_out8((l4util_in8(0x61) & ~0x02) | 0x01, 0x61);
184
185 l4util_out8(0xb0, 0x43); /* binary, mode 0, LSB/MSB, Ch 2 */
186 l4util_out8(calibrate_latch & 0xff, 0x42); /* LSB of count */
187 l4util_out8(calibrate_latch >> 8, 0x42); /* MSB of count */
188
189 {
190 unsigned long count;
191 unsigned long tt1, tt2;
192 unsigned long result;
193
194 tt1=apic_timer_read();
195 count = 0;
196 do
197 {
198 count++;
199 } while ((l4util_in8(0x61) & 0x20) == 0);
200 tt2=apic_timer_read();
201
202 result = (tt1-tt2)*apic_timer_divisor;
203
204 /* Error: ECTCNEVERSET */
205 if (count <= 1)
206 goto bad_ctc;
207
208 /* Error: ECPUTOOSLOW */
209 if (result <= calibrate_time)
210 goto bad_ctc;
211
212 __asm__ ("divl %1"
213 :"=a" (result)
214 :"r" (calibrate_time),
215 "0" (result),
216 "d" (0));
217
218 l4_scaler_apic_to_ms = result;
219
220 return result;
221 }
222
223 bad_ctc:
224 return 0;
225 }
226
227