1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * This software may be distributed and modified according to the terms of 5 * the GNU General Public License version 2. Note that NO WARRANTY is provided. 6 * See "LICENSE_GPLv2.txt" for details. 7 * 8 * @TAG(GD_GPL) 9 */ 10 11#include <config.h> 12#include <machine/io.h> 13#include <arch/machine.h> 14#include <arch/kernel/apic.h> 15#include <linker.h> 16#include <plat/machine/devices.h> 17#include <plat/machine/pit.h> 18 19static BOOT_CODE uint32_t 20apic_measure_freq(void) 21{ 22 pit_init(); 23 /* wait for 1st PIT wraparound */ 24 pit_wait_wraparound(); 25 26 /* start APIC timer countdown */ 27 apic_write_reg(APIC_TIMER_DIVIDE, 0xb); /* divisor = 1 */ 28 apic_write_reg(APIC_TIMER_COUNT, 0xffffffff); 29 30 /* wait for 2nd PIT wraparound */ 31 pit_wait_wraparound(); 32 33 /* calculate APIC/bus cycles per ms = frequency in kHz */ 34 return (0xffffffff - apic_read_reg(APIC_TIMER_CURRENT)) / PIT_WRAPAROUND_MS; 35} 36 37BOOT_CODE paddr_t 38apic_get_base_paddr(void) 39{ 40 apic_base_msr_t apic_base_msr; 41 42 apic_base_msr.words[0] = x86_rdmsr_low(IA32_APIC_BASE_MSR); 43 return apic_base_msr_get_base_addr(apic_base_msr); 44} 45 46BOOT_CODE bool_t 47apic_init(bool_t mask_legacy_irqs) 48{ 49 apic_version_t apic_version; 50 uint32_t num_lvt_entries; 51 uint32_t apic_khz; 52 53 if (!apic_enable()) { 54 return false; 55 } 56 57 apic_khz = apic_measure_freq(); 58 59 apic_version.words[0] = apic_read_reg(APIC_VERSION); 60 61 /* check for correct version (both APIC and x2APIC): 0x1X */ 62 if (apic_version_get_version(apic_version) >> 4 != 1) { 63 printf("APIC: apic_version must be 0x1X\n"); 64 return false; 65 } 66 67 /* check for correct number of LVT entries */ 68 num_lvt_entries = apic_version_get_max_lvt_entry(apic_version) + 1; 69 if (num_lvt_entries < 3) { 70 printf("APIC: number of LVT entries: %d\n", num_lvt_entries); 71 printf("APIC: number of LVT entries must be >= 3\n"); 72 return false; 73 } 74 75 /* initialise APIC timer */ 76 apic_write_reg(APIC_TIMER_DIVIDE, 0xb); /* divisor = 1 */ 77 apic_write_reg(APIC_TIMER_COUNT, apic_khz * CONFIG_TIMER_TICK_MS); 78 79 /* enable APIC using SVR register */ 80 apic_write_reg( 81 APIC_SVR, 82 apic_svr_new( 83 0, /* focus_processor_chk */ 84 1, /* enabled */ 85 int_spurious /* spurious_vector */ 86 ).words[0] 87 ); 88 89 /* mask/unmask LINT0 (used for legacy IRQ delivery) */ 90 apic_write_reg( 91 APIC_LVT_LINT0, 92 apic_lvt_new( 93 0, /* timer_mode */ 94 mask_legacy_irqs, /* masked */ 95 0, /* trigger_mode */ 96 0, /* remote_irr */ 97 0, /* pin_polarity */ 98 0, /* delivery_status */ 99 7, /* delivery_mode */ 100 0 /* vector */ 101 ).words[0] 102 ); 103 104 /* mask LINT1 (used for NMI delivery) */ 105 apic_write_reg( 106 APIC_LVT_LINT1, 107 apic_lvt_new( 108 0, /* timer_mode */ 109 1, /* masked */ 110 0, /* trigger_mode */ 111 0, /* remote_irr */ 112 0, /* pin_polarity */ 113 0, /* delivery_status */ 114 0, /* delivery_mode */ 115 0 /* vector */ 116 ).words[0] 117 ); 118 119 /* initialise timer */ 120 apic_write_reg( 121 APIC_LVT_TIMER, 122 apic_lvt_new( 123 1, /* timer_mode */ 124 0, /* masked */ 125 0, /* trigger_mode */ 126 0, /* remote_irr */ 127 0, /* pin_polarity */ 128 0, /* delivery_status */ 129 0, /* delivery_mode */ 130 int_timer /* vector */ 131 ).words[0] 132 ); 133 134 /* 135 printf("APIC: ID=0x%x\n", apic_read_reg(APIC_ID) >> 24); 136 printf("APIC: SVR=0x%x\n", apic_read_reg(APIC_SVR)); 137 printf("APIC: LVT_TIMER=0x%x\n", apic_read_reg(APIC_LVT_TIMER)); 138 printf("APIC: LVT_LINT0=0x%x\n", apic_read_reg(APIC_LVT_LINT0)); 139 printf("APIC: LVT_LINT1=0x%x\n", apic_read_reg(APIC_LVT_LINT1)); 140 printf("APIC: LVT_ERROR=0x%x\n", apic_read_reg(APIC_LVT_ERROR)); 141 printf("APIC: LVT_PERF_CNTR=0x%x\n", apic_read_reg(APIC_LVT_PERF_CNTR)); 142 printf("APIC: LVT_THERMAL=0x%x\n", apic_read_reg(APIC_LVT_THERMAL)); 143 */ 144 return true; 145} 146 147void apic_ack_active_interrupt(void) 148{ 149 apic_write_reg(APIC_EOI, 0); 150} 151