1 /**
2  * \file
3  * \brief APIC for X86
4  */
5 /*
6  * (c) 2008-2009 Adam Lackorzynski <adam@os.inf.tu-dresden.de>,
7  *               Frank Mehnert <fm3@os.inf.tu-dresden.de>
8  *     economic rights: Technische Universität Dresden (Germany)
9  * This file is part of TUD:OS and distributed under the terms of the
10  * GNU Lesser General Public License 2.1.
11  * Please see the COPYING-LGPL-2.1 file for details.
12  */
13 #ifndef __L4_UTIL_APIC_H
14 #define __L4_UTIL_APIC_H
15 
16 /*
17  * Local APIC programming library
18  *
19  * For documentation, see
20  *
21  * "Intel Architecture Software Developer's Manual", Volume 3, chapter 7.5:
22  * "Advanced Programmable Interrupt Controller (APIC)"
23  *
24  * Local APIC is present since
25  * - INTEL P6 (PPro)
26  * - AMD K7 (Athlon), Model 2
27  *
28  * In non-SMP-boards, local APIC is disabled, but
29  * can be activated by writing to a MSR register.
30  * For using APIC see packets cpufreq and l4rtl.
31  *
32  * See linux/include/asm-i386/i82489.h for further details.
33  */
34 
35 #define APIC_PHYS_BASE              0xFEE00000
36 #define APIC_MAP_BASE               0xA0200000
37 #define APIC_BASE_MSR               0x1b
38 
39 #define APIC_ID                     0x20
40 #define   GET_APIC_ID(x)              (((x)>>24)&0x0F)
41 #define APIC_LVR                    0x30
42 #define   GET_APIC_VERSION(x)         ((x)&0xFF)
43 #define APIC_TASKPRI                0x80
44 #define   APIC_TPRI_MASK              0xFF
45 #define APIC_EOI                    0xB0
46 #define APIC_LDR                    0xD0
47 #define   APIC_LDR_MASK               (0xFF<<24)
48 #define APIC_DFR                    0xE0
49 #define   SET_APIC_DFR(x)             ((x)<<28)
50 #define APIC_SPIV                   0xF0
51 #define APIC_LVTT                   0x320
52 #define APIC_LVTPC                  0x340
53 #define APIC_LVT0                   0x350
54 #define   SET_APIC_TIMER_BASE(x)      (((x)<<18))
55 #define   APIC_TIMER_BASE_DIV         0x2
56 #define APIC_LVT1                   0x360
57 #define APIC_LVTERR                 0x370
58 #define APIC_TMICT                  0x380
59 #define APIC_TMCCT                  0x390
60 #define APIC_TDCR                   0x3E0
61 
62 #define APIC_LVT_MASKED             (1<<16)
63 #define APIC_LVT_TIMER_PERIODIC     (1<<17)
64 #define APIC_TDR_DIV_1              0xB
65 #define APIC_TDR_DIV_2              0x0
66 #define APIC_TDR_DIV_4              0x1
67 #define APIC_TDR_DIV_8              0x2
68 #define APIC_TDR_DIV_16             0x3
69 #define APIC_TDR_DIV_32             0x8
70 #define APIC_TDR_DIV_64             0x9
71 #define APIC_TDR_DIV_128            0xA
72 
73 #include <l4/sys/compiler.h>
74 #include <l4/sys/types.h>
75 
76 EXTERN_C_BEGIN
77 
78 /* prototypes */
79 extern unsigned long apic_map_base;
80 extern unsigned long apic_timer_divisor;
81 
82 extern unsigned long l4_scaler_apic_to_ms;
83 
84 L4_CV void apic_show_registers(void);
85 L4_CV int  apic_check_working(void);
86 L4_CV void apic_activate_by_io(void);
87 L4_CV void apic_timer_set_divisor(int divisor);
88 
89 L4_CV unsigned long l4_calibrate_apic(void);
90 
91 EXTERN_C_END
92 
93 L4_INLINE void apic_write(unsigned long reg, unsigned long v);
94 L4_INLINE unsigned long apic_read(unsigned long reg);
95 L4_INLINE void apic_activate_by_msr(void);
96 L4_INLINE void apic_deactivate_by_msr(void);
97 L4_INLINE unsigned long apic_read_phys_address(void);
98 L4_INLINE int  apic_test_present(void);
99 L4_INLINE void apic_soft_enable(void);
100 L4_INLINE void apic_init(unsigned long map_addr);
101 L4_INLINE void apic_done(void);
102 L4_INLINE void apic_irq_ack(void);
103 
104 L4_INLINE void apic_lvt0_disable_irq(void);
105 L4_INLINE void apic_lvt0_enable_irq(void);
106 L4_INLINE void apic_lvt1_disable_irq(void);
107 L4_INLINE void apic_lvt1_enable_irq(void);
108 
109 L4_INLINE void apic_timer_write(unsigned long value);
110 L4_INLINE unsigned long apic_timer_read(void);
111 L4_INLINE void apic_timer_disable_irq(void);
112 L4_INLINE void apic_timer_enable_irq(void);
113 L4_INLINE void apic_timer_assign_irq(unsigned long vector);
114 L4_INLINE void apic_timer_set_periodic(void);
115 L4_INLINE void apic_timer_set_one_shot(void);
116 
117 L4_INLINE void apic_perf_disable_irq(void);
118 L4_INLINE void apic_perf_enable_irq(void);
119 L4_INLINE void apic_perf_assign_irq(unsigned long vector);
120 
121 
122 /* write APIC register */
123 L4_INLINE void
apic_write(unsigned long reg,unsigned long v)124 apic_write(unsigned long reg, unsigned long v)
125 {
126   *((volatile unsigned long *)(apic_map_base+reg))=v;
127 }
128 
129 
130 /* read APIC register */
131 L4_INLINE unsigned long
apic_read(unsigned long reg)132 apic_read(unsigned long reg)
133 {
134   return *((volatile unsigned long *)(apic_map_base+reg));
135 }
136 
137 
138 /* disable LINT0 */
139 L4_INLINE void
apic_lvt0_disable_irq(void)140 apic_lvt0_disable_irq(void)
141 {
142   unsigned long tmp_val;
143   tmp_val = apic_read(APIC_LVT0);
144   tmp_val |= APIC_LVT_MASKED;
145   apic_write(APIC_LVT0, tmp_val);
146 }
147 
148 
149 /* enable LINT0 */
150 L4_INLINE void
apic_lvt0_enable_irq(void)151 apic_lvt0_enable_irq(void)
152 {
153   unsigned long tmp_val;
154   tmp_val = apic_read(APIC_LVT0);
155   tmp_val &= ~(APIC_LVT_MASKED);
156   apic_write(APIC_LVT0, tmp_val);
157 }
158 
159 
160 /* disable LINT1 */
161 L4_INLINE void
apic_lvt1_disable_irq(void)162 apic_lvt1_disable_irq(void)
163 {
164   unsigned long tmp_val;
165   tmp_val = apic_read(APIC_LVT1);
166   tmp_val |= APIC_LVT_MASKED;
167   apic_write(APIC_LVT1, tmp_val);
168 }
169 
170 
171 /* enable LINT1 */
172 L4_INLINE void
apic_lvt1_enable_irq(void)173 apic_lvt1_enable_irq(void)
174 {
175   unsigned long tmp_val;
176   tmp_val = apic_read(APIC_LVT1);
177   tmp_val &= ~(APIC_LVT_MASKED);
178   apic_write(APIC_LVT1, tmp_val);
179 }
180 
181 
182 /* write APIC timer register */
183 L4_INLINE void
apic_timer_write(unsigned long value)184 apic_timer_write(unsigned long value)
185 {
186   apic_read(APIC_TMICT);
187   apic_write(APIC_TMICT,value);
188 }
189 
190 
191 /* read APIC timer register */
192 L4_INLINE unsigned long
apic_timer_read(void)193 apic_timer_read(void)
194 {
195   return apic_read(APIC_TMCCT);
196 }
197 
198 
199 /* disable IRQ when APIC timer passes 0 */
200 L4_INLINE void
apic_timer_disable_irq(void)201 apic_timer_disable_irq(void)
202 {
203   unsigned long tmp_val;
204   tmp_val = apic_read(APIC_LVTT);
205   tmp_val |= APIC_LVT_MASKED;
206   apic_write(APIC_LVTT, tmp_val);
207 }
208 
209 
210 /* enable IRQ when APIC timer passes 0 */
211 L4_INLINE void
apic_timer_enable_irq(void)212 apic_timer_enable_irq(void)
213 {
214   unsigned long tmp_val;
215   tmp_val = apic_read(APIC_LVTT);
216   tmp_val &= ~(APIC_LVT_MASKED);
217   apic_write(APIC_LVTT, tmp_val);
218 }
219 
220 
221 L4_INLINE void
apic_timer_set_periodic(void)222 apic_timer_set_periodic(void)
223 {
224   unsigned long tmp_val;
225   tmp_val = apic_read(APIC_LVTT);
226   tmp_val |= APIC_LVT_TIMER_PERIODIC;
227   tmp_val |= APIC_LVT_MASKED;
228   apic_write(APIC_LVTT, tmp_val);
229 }
230 
231 
232 L4_INLINE void
apic_timer_set_one_shot(void)233 apic_timer_set_one_shot(void)
234 {
235   unsigned long tmp_val;
236   tmp_val = apic_read(APIC_LVTT);
237   tmp_val &= ~APIC_LVT_TIMER_PERIODIC;
238   tmp_val |= APIC_LVT_MASKED;
239   apic_write(APIC_LVTT, tmp_val);
240 }
241 
242 
243 /* set vector of APIC timer irq */
244 L4_INLINE void
apic_timer_assign_irq(unsigned long vector)245 apic_timer_assign_irq(unsigned long vector)
246 {
247   unsigned long tmp_val;
248   tmp_val = apic_read(APIC_LVTT);
249   tmp_val &= 0xffffff00;
250   tmp_val |= vector;
251   tmp_val |= APIC_LVT_MASKED;
252   apic_write(APIC_LVTT, tmp_val);
253 }
254 
255 
256 /* disable IRQ when performance counter passes 0 */
257 L4_INLINE void
apic_perf_disable_irq(void)258 apic_perf_disable_irq(void)
259 {
260   unsigned long tmp_val;
261   tmp_val = apic_read(APIC_LVTPC);
262   tmp_val |= APIC_LVT_MASKED;
263   apic_write(APIC_LVTPC, tmp_val);
264 }
265 
266 
267 /* enable IRQ when performance counter passes 0 */
268 L4_INLINE void
apic_perf_enable_irq(void)269 apic_perf_enable_irq(void)
270 {
271   unsigned long tmp_val;
272   tmp_val = apic_read(APIC_LVTPC);
273   tmp_val &= ~(APIC_LVT_MASKED);
274   apic_write(APIC_LVTPC, tmp_val);
275 }
276 
277 
278 /* set vector of performance counter irq */
279 L4_INLINE void
apic_perf_assign_irq(unsigned long vector)280 apic_perf_assign_irq(unsigned long vector)
281 {
282   unsigned long tmp_val;
283   tmp_val = apic_read(APIC_LVTPC);
284   tmp_val &= 0xffffff00;
285   tmp_val |= vector;
286   tmp_val |= APIC_LVT_MASKED;
287   apic_write(APIC_LVTPC, tmp_val);
288 }
289 
290 
291 /* activate APIC by writing to appropriate MSR */
292 L4_INLINE void
apic_activate_by_msr(void)293 apic_activate_by_msr(void)
294 {
295   unsigned long low;
296   unsigned long high;
297 
298   /* rdmsr */
299   asm volatile(".byte 0xf; .byte 0x32\n"
300                :"=a" (low),
301                 "=d" (high)
302                :"c" (APIC_BASE_MSR)
303                );
304 
305   low |= 0x800;                         /* activate APIC */
306   low &= 0x00000fff;
307   low |= (APIC_PHYS_BASE & 0xfffff000); /* set address */
308 
309   /* wrmsr */
310   asm volatile(".byte 0xf; .byte 0x30\n"
311                :
312                :"c" (APIC_BASE_MSR),
313                 "a" (low),
314                 "d" (high)
315                );
316 }
317 
318 
319 /* deactivate APIC by writing to appropriate MSR */
320 L4_INLINE void
apic_deactivate_by_msr(void)321 apic_deactivate_by_msr(void)
322 {
323   unsigned long low;
324   unsigned long high;
325 
326   /* rdmsr */
327   asm volatile(".byte 0xf; .byte 0x32\n"
328                :"=a" (low),
329                 "=d" (high)
330                :"c" (APIC_BASE_MSR)
331                );
332 
333   low  &= 0xfffff7ff;                    /* deactivate APIC */
334 
335   /* wrmsr */
336   asm volatile(".byte 0xf; .byte 0x30\n"
337                :
338                :"c" (APIC_BASE_MSR),
339                 "a" (low),
340                 "d" (high)
341                );
342 }
343 
344 
345 /* read memory mapped address of apic */
346 L4_INLINE unsigned long
apic_read_phys_address(void)347 apic_read_phys_address(void)
348 {
349   unsigned long low;
350   unsigned long high;
351 
352   /* rdmsr */
353   asm volatile(".byte 0xf; .byte 0x32\n"
354                :"=a" (low),
355                 "=d" (high)
356                :"c" (APIC_BASE_MSR)
357                );
358 
359   return (low  &= 0xfffff000);
360 }
361 
362 
363 /* test if APIC present */
364 L4_INLINE int
apic_test_present(void)365 apic_test_present(void)
366 {
367   unsigned int dummy;
368   unsigned int capability;
369 
370   asm volatile("pushl %%ebx ; cpuid ; popl %%ebx"
371              : "=a" (dummy),
372                "=c" (dummy),
373                "=d" (capability)
374              : "a" (0x00000001)
375              : "cc");
376 
377   return ((capability & 1<<9) !=0);
378 }
379 
380 
381 L4_INLINE void
apic_soft_enable(void)382 apic_soft_enable(void)
383 {
384   unsigned long tmp_val;
385   tmp_val = apic_read(APIC_SPIV);
386   tmp_val |= (1<<8);          /* enable APIC */
387   tmp_val &= ~(1<<9);         /* enable Focus Processor Checking */
388   tmp_val |= 0xff;            /* Set spurious IRQ vector to 0xff */
389   apic_write(APIC_SPIV, tmp_val);
390 }
391 
392 
393 L4_INLINE void
apic_init(unsigned long base_addr)394 apic_init(unsigned long base_addr)
395 {
396   apic_map_base = base_addr;
397 }
398 
399 
400 L4_INLINE void
apic_done(void)401 apic_done(void)
402 {
403   apic_map_base = 0;
404 }
405 
406 
407 L4_INLINE void
apic_irq_ack(void)408 apic_irq_ack(void)
409 {
410   apic_read(APIC_SPIV);
411   apic_write(APIC_EOI, 0);
412 }
413 
414 
415 #endif /* __L4_UTIL_APIC_H */
416