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