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
apic_measure_freq(void)19 static 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
apic_get_base_paddr(void)36 BOOT_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
apic_init(bool_t mask_legacy_irqs)44 BOOT_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
apic_ack_active_interrupt(void)174 void apic_ack_active_interrupt(void)
175 {
176 apic_write_reg(APIC_EOI, 0);
177 }
178