/*
* Copyright (c) 2011-2017 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/*
* Mask used to check that local APICS are internal.
*/
#define LAPIC_VERSION_MASK 0x10
/*
* Common bits for registers in the local vector table.
*/
#define LAPIC_LVT_DELIVERY_FIXED 0x00000000
#define LAPIC_LVT_DELIVERY_NMI 0x00000400
#define LAPIC_LVT_DELIVERY_EXTINT 0x00000700
#define LAPIC_LVT_MASK_INTR 0x00010000
/*
* LVT timer register bits.
*/
#define LAPIC_LVT_TIMER_PERIODIC 0x00020000
/*
* Various values related to the local APIC timer.
*/
#define LAPIC_TIMER_DCR_DIV1 0x0000000b
#define LAPIC_TIMER_COUNT_MAX 0xffffffff
/*
* Delay used to calibrate the local APIC timer, in microseconds.
*/
#define LAPIC_TIMER_CAL_DELAY 100000
/*
* Spurious-interrupt vector register bits.
*/
#define LAPIC_SVR_SOFT_EN 0x00000100
/*
* Interrupt command register (lower word) bits.
*/
#define LAPIC_ICR_VECTOR_MASK 0x000000ff
#define LAPIC_ICR_DELIVERY_INIT 0x00000500
#define LAPIC_ICR_DELIVERY_STARTUP 0x00000600
#define LAPIC_ICR_STATUS_PENDING 0x00001000
#define LAPIC_ICR_LEVEL_ASSERT 0x00004000
#define LAPIC_ICR_TRIGGER_LEVEL 0x00008000
#define LAPIC_ICR_DEST_SELF 0x00040000
#define LAPIC_ICR_DEST_ALL_WITH_SELF 0x00080000
#define LAPIC_ICR_DEST_ALL_EXCEPT_SELF 0x000c0000
#define LAPIC_ICR_DEST_MASK 0x000c0000
#define LAPIC_ICR_RESERVED 0xfff32000
/*
* ICR destination shift and mask.
*/
#define LAPIC_DEST_SHIFT 24
#define LAPIC_DEST_MASK 0xff000000
/*
* Local APIC registers are accessed with 32-bits loads/stores aligned on
* 128 bits.
*/
struct lapic_register {
uint32_t reg;
uint32_t reserved0;
uint32_t reserved1;
uint32_t reserved2;
};
/*
* Local APIC register map.
*/
struct lapic_map {
const struct lapic_register reserved0;
const struct lapic_register reserved1;
/*
* Some processors don't allow writing to this register, and the
* specification explicitely discourages modifications. Consider it
* read only.
*/
const struct lapic_register id;
const struct lapic_register version;
const struct lapic_register reserved2;
const struct lapic_register reserved3;
const struct lapic_register reserved4;
const struct lapic_register reserved5;
struct lapic_register tpr;
const struct lapic_register reserved6; /* APR */
const struct lapic_register ppr;
struct lapic_register eoi;
const struct lapic_register reserved7; /* RRD */
struct lapic_register ldr;
struct lapic_register dfr;
struct lapic_register svr;
const struct lapic_register isr0;
const struct lapic_register isr1;
const struct lapic_register isr2;
const struct lapic_register isr3;
const struct lapic_register isr4;
const struct lapic_register isr5;
const struct lapic_register isr6;
const struct lapic_register isr7;
const struct lapic_register tmr0;
const struct lapic_register tmr1;
const struct lapic_register tmr2;
const struct lapic_register tmr3;
const struct lapic_register tmr4;
const struct lapic_register tmr5;
const struct lapic_register tmr6;
const struct lapic_register tmr7;
const struct lapic_register irr0;
const struct lapic_register irr1;
const struct lapic_register irr2;
const struct lapic_register irr3;
const struct lapic_register irr4;
const struct lapic_register irr5;
const struct lapic_register irr6;
const struct lapic_register irr7;
struct lapic_register esr;
const struct lapic_register reserved8;
const struct lapic_register reserved9;
const struct lapic_register reserved10;
const struct lapic_register reserved11;
const struct lapic_register reserved12;
const struct lapic_register reserved13;
struct lapic_register lvt_cmci;
struct lapic_register icr_low;
struct lapic_register icr_high;
struct lapic_register lvt_timer;
const struct lapic_register reserved14; /* Thermal sensor register */
struct lapic_register lvt_pmc; /* Performance counters register */
struct lapic_register lvt_lint0;
struct lapic_register lvt_lint1;
struct lapic_register lvt_error;
struct lapic_register timer_icr;
const struct lapic_register timer_ccr;
const struct lapic_register reserved16;
const struct lapic_register reserved17;
const struct lapic_register reserved18;
const struct lapic_register reserved19;
struct lapic_register timer_dcr;
const struct lapic_register reserved20;
};
/*
* Address where local APIC registers are mapped.
*/
static volatile struct lapic_map *lapic_map __read_mostly;
/*
* Base frequency of the local APIC timer.
*/
static uint32_t lapic_bus_freq __read_mostly;
static uint32_t
lapic_read(const volatile struct lapic_register *r)
{
return r->reg;
}
static void
lapic_write(volatile struct lapic_register *r, uint32_t value)
{
r->reg = value;
}
static void __init
lapic_compute_freq(void)
{
uint32_t c1, c2;
lapic_write(&lapic_map->svr, LAPIC_SVR_SOFT_EN | TRAP_LAPIC_SPURIOUS);
lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1);
/* The APIC timer counter should never wrap around here */
lapic_write(&lapic_map->timer_icr, LAPIC_TIMER_COUNT_MAX);
c1 = lapic_read(&lapic_map->timer_ccr);
cpu_delay(LAPIC_TIMER_CAL_DELAY);
c2 = lapic_read(&lapic_map->timer_ccr);
lapic_bus_freq = (c1 - c2) * (1000000 / LAPIC_TIMER_CAL_DELAY);
log_info("lapic: bus frequency: %u.%02u MHz", lapic_bus_freq / 1000000,
lapic_bus_freq % 1000000);
lapic_write(&lapic_map->timer_icr, lapic_bus_freq / CLOCK_FREQ);
lapic_write(&lapic_map->svr, 0);
}
void
lapic_eoi(void)
{
lapic_write(&lapic_map->eoi, 0);
}
static void __init
lapic_setup_registers(void)
{
/*
* LVT mask bits can only be cleared when the local APIC is enabled.
* They are kept disabled while the local APIC is disabled.
*/
lapic_write(&lapic_map->svr, LAPIC_SVR_SOFT_EN | TRAP_LAPIC_SPURIOUS);
lapic_write(&lapic_map->tpr, 0);
lapic_write(&lapic_map->eoi, 0);
lapic_write(&lapic_map->esr, 0);
lapic_write(&lapic_map->lvt_timer, LAPIC_LVT_TIMER_PERIODIC
| TRAP_LAPIC_TIMER);
lapic_write(&lapic_map->lvt_lint0, LAPIC_LVT_MASK_INTR);
lapic_write(&lapic_map->lvt_lint1, LAPIC_LVT_MASK_INTR);
lapic_write(&lapic_map->lvt_error, TRAP_LAPIC_ERROR);
lapic_write(&lapic_map->timer_dcr, LAPIC_TIMER_DCR_DIV1);
lapic_write(&lapic_map->timer_icr, lapic_bus_freq / CLOCK_FREQ);
lapic_write(&lapic_map->lvt_pmc, TRAP_LAPIC_PMC_OF);
}
void __init
lapic_setup(uint32_t map_addr)
{
uint32_t value;
lapic_map = vm_kmem_map_pa(map_addr, sizeof(*lapic_map), NULL, NULL);
if (lapic_map == NULL) {
panic("lapic: unable to map registers in kernel map");
}
value = lapic_read(&lapic_map->version);
if ((value & LAPIC_VERSION_MASK) != LAPIC_VERSION_MASK) {
panic("lapic: external local APIC not supported");
}
lapic_compute_freq();
lapic_setup_registers();
}
void __init
lapic_ap_setup(void)
{
lapic_setup_registers();
}
static void
lapic_ipi(uint32_t apic_id, uint32_t icr)
{
unsigned long flags;
cpu_intr_save(&flags);
if ((icr & LAPIC_ICR_DEST_MASK) == 0) {
lapic_write(&lapic_map->icr_high, apic_id << LAPIC_DEST_SHIFT);
}
lapic_write(&lapic_map->icr_low, icr & ~LAPIC_ICR_RESERVED);
cpu_intr_restore(flags);
}
static void
lapic_ipi_wait(void)
{
uint32_t value;
do {
value = lapic_read(&lapic_map->icr_low);
cpu_pause();
} while (value & LAPIC_ICR_STATUS_PENDING);
}
void
lapic_ipi_init_assert(uint32_t apic_id)
{
lapic_ipi(apic_id, LAPIC_ICR_TRIGGER_LEVEL | LAPIC_ICR_LEVEL_ASSERT
| LAPIC_ICR_DELIVERY_INIT);
lapic_ipi_wait();
}
void
lapic_ipi_init_deassert(uint32_t apic_id)
{
lapic_ipi(apic_id, LAPIC_ICR_TRIGGER_LEVEL | LAPIC_ICR_DELIVERY_INIT);
lapic_ipi_wait();
}
void
lapic_ipi_startup(uint32_t apic_id, uint32_t vector)
{
lapic_ipi(apic_id, LAPIC_ICR_DELIVERY_STARTUP
| (vector & LAPIC_ICR_VECTOR_MASK));
lapic_ipi_wait();
}
void
lapic_ipi_send(uint32_t apic_id, uint32_t vector)
{
lapic_ipi_wait();
lapic_ipi(apic_id, vector & LAPIC_ICR_VECTOR_MASK);
}
void
lapic_ipi_broadcast(uint32_t vector)
{
lapic_ipi_wait();
lapic_ipi(0, LAPIC_ICR_DEST_ALL_EXCEPT_SELF
| (vector & LAPIC_ICR_VECTOR_MASK));
}
void
lapic_pmc_of_intr(struct trap_frame *frame)
{
(void)frame;
#ifdef CONFIG_PERFMON
perfmon_of_intr();
/* Resets the interupt as it is auto cleared when it fires */
lapic_write(&lapic_map->lvt_pmc, TRAP_LAPIC_PMC_OF);
#endif
lapic_eoi();
}
void
lapic_timer_intr(struct trap_frame *frame)
{
(void)frame;
lapic_eoi();
clock_tick_intr();
}
void
lapic_error_intr(struct trap_frame *frame)
{
uint32_t esr;
(void)frame;
esr = lapic_read(&lapic_map->esr);
log_err("lapic: error on cpu%u: esr:%08x", cpu_id(), esr);
lapic_write(&lapic_map->esr, 0);
lapic_eoi();
}
void
lapic_spurious_intr(struct trap_frame *frame)
{
(void)frame;
log_warning("lapic: spurious interrupt");
/* No EOI for this interrupt */
}