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