1/* 2 * Copyright 2014, General Dynamics C4 Systems 3 * 4 * SPDX-License-Identifier: GPL-2.0-only 5 */ 6 7#include <config.h> 8#include <machine/io.h> 9#include <arch/machine.h> 10#include <arch/kernel/apic.h> 11#include <linker.h> 12#include <plat/machine/devices.h> 13#include <plat/machine/pit.h> 14 15#define CPUID_TSC_DEADLINE_BIT 24u 16#define APIC_TIMER_MODE_ONE_SHOT 0 17#define APIC_TIMER_MODE_TSC_DEADLINE 2 18 19static BOOT_CODE uint32_t apic_measure_freq(void) 20{ 21 pit_init(); 22 /* wait for 1st PIT wraparound */ 23 pit_wait_wraparound(); 24 25 /* start APIC timer countdown */ 26 apic_write_reg(APIC_TIMER_DIVIDE, 0xb); /* divisor = 1 */ 27 apic_write_reg(APIC_TIMER_COUNT, 0xffffffff); 28 29 /* wait for 2nd PIT wraparound */ 30 pit_wait_wraparound(); 31 32 /* calculate APIC/bus cycles per ms = frequency in kHz */ 33 return (0xffffffff - apic_read_reg(APIC_TIMER_CURRENT)) / PIT_WRAPAROUND_MS; 34} 35 36BOOT_CODE paddr_t apic_get_base_paddr(void) 37{ 38 apic_base_msr_t apic_base_msr; 39 40 apic_base_msr.words[0] = x86_rdmsr_low(IA32_APIC_BASE_MSR); 41 return apic_base_msr_get_base_addr(apic_base_msr); 42} 43 44BOOT_CODE bool_t apic_init(bool_t mask_legacy_irqs) 45{ 46 apic_version_t apic_version; 47 uint32_t num_lvt_entries; 48 uint32_t apic_khz; 49 50 if (!apic_enable()) { 51 return false; 52 } 53 54#ifdef CONFIG_KERNEL_MCS 55 /* find tsc KHz */ 56 x86KStscMhz = tsc_init(); 57 58 /* can we use tsc deadline mode? */ 59 uint32_t cpuid = x86_cpuid_ecx(0x1, 0x0); 60 if (!(cpuid & BIT(CPUID_TSC_DEADLINE_BIT))) { 61 apic_khz = apic_measure_freq(); 62 x86KSapicRatio = div64((uint64_t)x86KStscMhz * 1000llu, apic_khz); 63 printf("Apic Khz %lu, TSC Mhz %lu, ratio %lu\n", (long) apic_khz, (long) x86KStscMhz, (long) x86KSapicRatio); 64 } else { 65 // use tsc deadline mode 66 x86KSapicRatio = 0; 67 } 68#else 69 apic_khz = apic_measure_freq(); 70#endif 71 apic_version.words[0] = apic_read_reg(APIC_VERSION); 72 73 /* check for correct version (both APIC and x2APIC): 0x1X */ 74 if (apic_version_get_version(apic_version) >> 4 != 1) { 75 printf("APIC: apic_version must be 0x1X\n"); 76 return false; 77 } 78 79#ifdef CONFIG_KERNEL_MCS 80 if (x86KSapicRatio != 0) { 81 /* initialise APIC timer */ 82 apic_write_reg(APIC_TIMER_DIVIDE, 0xb); /* divisor = 1 */ 83 } 84#endif 85 86 /* check for correct number of LVT entries */ 87 num_lvt_entries = apic_version_get_max_lvt_entry(apic_version) + 1; 88 if (num_lvt_entries < 3) { 89 printf("APIC: number of LVT entries: %d\n", num_lvt_entries); 90 printf("APIC: number of LVT entries must be >= 3\n"); 91 return false; 92 } 93 94#ifndef CONFIG_KERNEL_MCS 95 /* initialise APIC timer */ 96 apic_write_reg(APIC_TIMER_DIVIDE, 0xb); /* divisor = 1 */ 97 apic_write_reg(APIC_TIMER_COUNT, apic_khz * CONFIG_TIMER_TICK_MS); 98#endif 99 100 /* enable APIC using SVR register */ 101 apic_write_reg( 102 APIC_SVR, 103 apic_svr_new( 104 0, /* focus_processor_chk */ 105 1, /* enabled */ 106 int_spurious /* spurious_vector */ 107 ).words[0] 108 ); 109 110 /* mask/unmask LINT0 (used for legacy IRQ delivery) */ 111 apic_write_reg( 112 APIC_LVT_LINT0, 113 apic_lvt_new( 114 0, /* timer_mode */ 115 mask_legacy_irqs, /* masked */ 116 0, /* trigger_mode */ 117 0, /* remote_irr */ 118 0, /* pin_polarity */ 119 0, /* delivery_status */ 120 7, /* delivery_mode */ 121 0 /* vector */ 122 ).words[0] 123 ); 124 125 /* mask LINT1 (used for NMI delivery) */ 126 apic_write_reg( 127 APIC_LVT_LINT1, 128 apic_lvt_new( 129 0, /* timer_mode */ 130 1, /* masked */ 131 0, /* trigger_mode */ 132 0, /* remote_irr */ 133 0, /* pin_polarity */ 134 0, /* delivery_status */ 135 0, /* delivery_mode */ 136 0 /* vector */ 137 ).words[0] 138 ); 139 140 /* initialise timer */ 141#ifdef CONFIG_KERNEL_MCS 142 uint32_t timer_mode = x86KSapicRatio == 0 ? APIC_TIMER_MODE_TSC_DEADLINE : 143 APIC_TIMER_MODE_ONE_SHOT; 144#else 145 uint32_t timer_mode = 1; 146#endif 147 apic_write_reg( 148 APIC_LVT_TIMER, 149 apic_lvt_new( 150 timer_mode, 151 0, /* masked */ 152 0, /* trigger_mode */ 153 0, /* remote_irr */ 154 0, /* pin_polarity */ 155 0, /* delivery_status */ 156 0, /* delivery_mode */ 157 int_timer /* vector */ 158 ).words[0] 159 ); 160 161 /* 162 printf("APIC: ID=0x%x\n", apic_read_reg(APIC_ID) >> 24); 163 printf("APIC: SVR=0x%x\n", apic_read_reg(APIC_SVR)); 164 printf("APIC: LVT_TIMER=0x%x\n", apic_read_reg(APIC_LVT_TIMER)); 165 printf("APIC: LVT_LINT0=0x%x\n", apic_read_reg(APIC_LVT_LINT0)); 166 printf("APIC: LVT_LINT1=0x%x\n", apic_read_reg(APIC_LVT_LINT1)); 167 printf("APIC: LVT_ERROR=0x%x\n", apic_read_reg(APIC_LVT_ERROR)); 168 printf("APIC: LVT_PERF_CNTR=0x%x\n", apic_read_reg(APIC_LVT_PERF_CNTR)); 169 printf("APIC: LVT_THERMAL=0x%x\n", apic_read_reg(APIC_LVT_THERMAL)); 170 */ 171 return true; 172} 173 174void apic_ack_active_interrupt(void) 175{ 176 apic_write_reg(APIC_EOI, 0); 177} 178