/** * \file * \brief APIC for AMD64 */ /* * (c) 2008-2009 Adam Lackorzynski , * Torsten Frenzel * economic rights: Technische Universität Dresden (Germany) * This file is part of TUD:OS and distributed under the terms of the * GNU Lesser General Public License 2.1. * Please see the COPYING-LGPL-2.1 file for details. */ #ifndef __L4_UTIL_APIC_H #define __L4_UTIL_APIC_H /* * Local APIC programming library * * For documentation, see * * "Intel Architecture Software Developer's Manual", Volume 3, chapter 7.5: * "Advanced Programmable Interrupt Controller (APIC)" * * Local APIC is present since * - INTEL P6 (PPro) * - AMD K7 (Athlon), Model 2 * * In non-SMP-boards, local APIC is disabled, but * can be activated by writing to a MSR register. * For using APIC see packets cpufreq and l4rtl. * * See linux/include/asm-i386/i82489.h for further details. */ #define APIC_PHYS_BASE 0xFEE00000 #define APIC_MAP_BASE 0xA0200000 #define APIC_BASE_MSR 0x1b #define APIC_ID 0x20 #define GET_APIC_ID(x) (((x)>>24)&0x0F) #define APIC_LVR 0x30 #define GET_APIC_VERSION(x) ((x)&0xFF) #define APIC_TASKPRI 0x80 #define APIC_TPRI_MASK 0xFF #define APIC_EOI 0xB0 #define APIC_LDR 0xD0 #define APIC_LDR_MASK (0xFF<<24) #define APIC_DFR 0xE0 #define SET_APIC_DFR(x) ((x)<<28) #define APIC_SPIV 0xF0 #define APIC_LVTT 0x320 #define APIC_LVTPC 0x340 #define APIC_LVT0 0x350 #define SET_APIC_TIMER_BASE(x) (((x)<<18)) #define APIC_TIMER_BASE_DIV 0x2 #define APIC_LVT1 0x360 #define APIC_LVTERR 0x370 #define APIC_TMICT 0x380 #define APIC_TMCCT 0x390 #define APIC_TDCR 0x3E0 #define APIC_LVT_MASKED (1<<16) #define APIC_LVT_TIMER_PERIODIC (1<<17) #define APIC_TDR_DIV_1 0xB #define APIC_TDR_DIV_2 0x0 #define APIC_TDR_DIV_4 0x1 #define APIC_TDR_DIV_8 0x2 #define APIC_TDR_DIV_16 0x3 #define APIC_TDR_DIV_32 0x8 #define APIC_TDR_DIV_64 0x9 #define APIC_TDR_DIV_128 0xA #include #include EXTERN_C_BEGIN /* prototypes */ extern unsigned long apic_map_base; extern unsigned long apic_timer_divisor; extern unsigned long l4_scaler_apic_to_ms; L4_CV void apic_show_registers(void); L4_CV int apic_check_working(void); L4_CV void apic_activate_by_io(void); L4_CV void apic_timer_set_divisor(int divisor); L4_CV unsigned long l4_calibrate_apic(void); EXTERN_C_END L4_INLINE void apic_write(unsigned long reg, unsigned long v); L4_INLINE unsigned long apic_read(unsigned long reg); L4_INLINE void apic_activate_by_msr(void); L4_INLINE void apic_deactivate_by_msr(void); L4_INLINE unsigned long apic_read_phys_address(void); L4_INLINE int apic_test_present(void); L4_INLINE void apic_soft_enable(void); L4_INLINE void apic_init(unsigned long map_addr); L4_INLINE void apic_done(void); L4_INLINE void apic_irq_ack(void); L4_INLINE void apic_lvt0_disable_irq(void); L4_INLINE void apic_lvt0_enable_irq(void); L4_INLINE void apic_lvt1_disable_irq(void); L4_INLINE void apic_lvt1_enable_irq(void); L4_INLINE void apic_timer_write(unsigned long value); L4_INLINE unsigned long apic_timer_read(void); L4_INLINE void apic_timer_disable_irq(void); L4_INLINE void apic_timer_enable_irq(void); L4_INLINE void apic_timer_assign_irq(unsigned long vector); L4_INLINE void apic_timer_set_periodic(void); L4_INLINE void apic_timer_set_one_shot(void); L4_INLINE void apic_perf_disable_irq(void); L4_INLINE void apic_perf_enable_irq(void); L4_INLINE void apic_perf_assign_irq(unsigned long vector); /* write APIC register */ L4_INLINE void apic_write(unsigned long reg, unsigned long v) { *((volatile unsigned long *)(apic_map_base+reg))=v; } /* read APIC register */ L4_INLINE unsigned long apic_read(unsigned long reg) { return *((volatile unsigned long *)(apic_map_base+reg)); } /* disable LINT0 */ L4_INLINE void apic_lvt0_disable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVT0); tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVT0, tmp_val); } /* enable LINT0 */ L4_INLINE void apic_lvt0_enable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVT0); tmp_val &= ~(APIC_LVT_MASKED); apic_write(APIC_LVT0, tmp_val); } /* disable LINT1 */ L4_INLINE void apic_lvt1_disable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVT1); tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVT1, tmp_val); } /* enable LINT1 */ L4_INLINE void apic_lvt1_enable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVT1); tmp_val &= ~(APIC_LVT_MASKED); apic_write(APIC_LVT1, tmp_val); } /* write APIC timer register */ L4_INLINE void apic_timer_write(unsigned long value) { apic_read(APIC_TMICT); apic_write(APIC_TMICT,value); } /* read APIC timer register */ L4_INLINE unsigned long apic_timer_read(void) { return apic_read(APIC_TMCCT); } /* disable IRQ when APIC timer passes 0 */ L4_INLINE void apic_timer_disable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTT); tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTT, tmp_val); } /* enable IRQ when APIC timer passes 0 */ L4_INLINE void apic_timer_enable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTT); tmp_val &= ~(APIC_LVT_MASKED); apic_write(APIC_LVTT, tmp_val); } L4_INLINE void apic_timer_set_periodic(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTT); tmp_val |= APIC_LVT_TIMER_PERIODIC; tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTT, tmp_val); } L4_INLINE void apic_timer_set_one_shot(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTT); tmp_val &= ~APIC_LVT_TIMER_PERIODIC; tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTT, tmp_val); } /* set vector of APIC timer irq */ L4_INLINE void apic_timer_assign_irq(unsigned long vector) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTT); tmp_val &= 0xffffff00; tmp_val |= vector; tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTT, tmp_val); } /* disable IRQ when performance counter passes 0 */ L4_INLINE void apic_perf_disable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTPC); tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTPC, tmp_val); } /* enable IRQ when performance counter passes 0 */ L4_INLINE void apic_perf_enable_irq(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTPC); tmp_val &= ~(APIC_LVT_MASKED); apic_write(APIC_LVTPC, tmp_val); } /* set vector of performance counter irq */ L4_INLINE void apic_perf_assign_irq(unsigned long vector) { unsigned long tmp_val; tmp_val = apic_read(APIC_LVTPC); tmp_val &= 0xffffff00; tmp_val |= vector; tmp_val |= APIC_LVT_MASKED; apic_write(APIC_LVTPC, tmp_val); } /* activate APIC by writing to appropriate MSR */ L4_INLINE void apic_activate_by_msr(void) { unsigned long low; unsigned long high; /* rdmsr */ asm volatile(".byte 0xf; .byte 0x32\n" :"=a" (low), "=d" (high) :"c" (APIC_BASE_MSR) ); low |= 0x800; /* activate APIC */ low &= 0x00000fff; low |= (APIC_PHYS_BASE & 0xfffff000); /* set address */ /* wrmsr */ asm volatile(".byte 0xf; .byte 0x30\n" : :"c" (APIC_BASE_MSR), "a" (low), "d" (high) ); } /* deactivate APIC by writing to appropriate MSR */ L4_INLINE void apic_deactivate_by_msr(void) { unsigned long low; unsigned long high; /* rdmsr */ asm volatile(".byte 0xf; .byte 0x32\n" :"=a" (low), "=d" (high) :"c" (APIC_BASE_MSR) ); low &= 0xfffff7ff; /* deactivate APIC */ /* wrmsr */ asm volatile(".byte 0xf; .byte 0x30\n" : :"c" (APIC_BASE_MSR), "a" (low), "d" (high) ); } /* read memory mapped address of apic */ L4_INLINE unsigned long apic_read_phys_address(void) { unsigned long low; unsigned long high; /* rdmsr */ asm volatile(".byte 0xf; .byte 0x32\n" :"=a" (low), "=d" (high) :"c" (APIC_BASE_MSR) ); return (low &= 0xfffff000); } /* test if APIC present */ L4_INLINE int apic_test_present(void) { unsigned int dummy; unsigned int capability; asm volatile("cpuid" : "=a" (dummy), "=d" (capability) : "a" (0x00000001) : "cc", "rbx", "rcx"); return ((capability & 1<<9) !=0); } L4_INLINE void apic_soft_enable(void) { unsigned long tmp_val; tmp_val = apic_read(APIC_SPIV); tmp_val |= (1<<8); /* enable APIC */ tmp_val &= ~(1<<9); /* enable Focus Processor Checking */ tmp_val |= 0xff; /* Set spurious IRQ vector to 0xff */ apic_write(APIC_SPIV, tmp_val); } L4_INLINE void apic_init(unsigned long base_addr) { apic_map_base = base_addr; } L4_INLINE void apic_done(void) { apic_map_base = 0; } L4_INLINE void apic_irq_ack(void) { apic_read(APIC_SPIV); apic_write(APIC_EOI, 0); } #endif /* __L4_UTIL_APIC_H */