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