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