summaryrefslogtreecommitdiff
path: root/payloads/libpayload/arch/x86
diff options
context:
space:
mode:
authorRaul E Rangel <rrangel@chromium.org>2018-08-20 12:31:11 -0600
committerMartin Roth <martinroth@google.com>2018-09-12 14:15:11 +0000
commit24ae85c3ffb48c72f67a514dcb843c1de54e5416 (patch)
tree7990b569495136ce505de044f6c27e265ff80233 /payloads/libpayload/arch/x86
parentac8ebd0e73774c83bfa5cb3dd460c662000e0d19 (diff)
libpayload/x86/apic: Add an apic_delay method and calibrate the timer
The apic_delay method will halt the CPU and wait for a timer interrupt to fire. I went with usec because nsec is too granular to guarantee. This method will be called from an arch_ndelay() method when the delay is large enough to justify a sleep. BUG=b:109749762 TEST=Tested it on grunt by changing the _delay method to call apic_delay(). Change-Id: I80363f06bdb22d0907f895885e607fde1c4c468d Signed-off-by: Raul E Rangel <rrangel@chromium.org> Reviewed-on: https://review.coreboot.org/28242 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Martin Roth <martinroth@google.com>
Diffstat (limited to 'payloads/libpayload/arch/x86')
-rw-r--r--payloads/libpayload/arch/x86/apic.c77
1 files changed, 77 insertions, 0 deletions
diff --git a/payloads/libpayload/arch/x86/apic.c b/payloads/libpayload/arch/x86/apic.c
index d5cf6ae14f..079f456eec 100644
--- a/payloads/libpayload/arch/x86/apic.c
+++ b/payloads/libpayload/arch/x86/apic.c
@@ -61,8 +61,13 @@
#define APIC_LVT_SIZE 0x010
+#define APIC_TIMER_VECTOR 0x20
+
static uint32_t apic_bar;
static int _apic_initialized;
+// TODO: Build a lookup table to avoid calculating it.
+static uint32_t ticks_per_ms;
+static volatile uint8_t timer_waiting;
enum APIC_CAPABILITY {
DISABLED = 0,
@@ -95,6 +100,47 @@ uint8_t apic_id(void)
return id;
}
+void apic_delay(unsigned int usec)
+{
+ die_if(!ticks_per_ms, "apic_init_timer was not run.");
+ die_if(timer_waiting, "timer already started.");
+ die_if(!interrupts_enabled(), "Interrupts disabled.");
+
+ /* The order is important so we don't underflow */
+ uint64_t ticks = usec * ticks_per_ms / USECS_PER_MSEC;
+
+ /* Not enough resolution */
+ if (!ticks)
+ return;
+
+ /* Disable interrupts so we don't get a race condition between
+ * starting the timer and the hlt instruction. */
+ disable_interrupts();
+
+ timer_waiting = 1;
+
+ apic_write32(APIC_TIMER_INIT_COUNT, ticks);
+
+ /* Loop in case another interrupt has fired and resumed execution. */
+ do {
+ asm volatile(
+ "sti\n\t"
+ "hlt\n\t"
+ /* Disable interrupts to prevent a race condition
+ * between checking timer_waiting and executing the hlt
+ * instruction again. */
+ "cli\n\t");
+ } while (timer_waiting);
+
+ /* Leave hardware interrupts enabled. */
+ enable_interrupts();
+}
+
+static void timer_interrupt_handler(u8 vector)
+{
+ timer_waiting = 0;
+}
+
void apic_eoi(void)
{
die_if(!apic_bar, "APIC is not initialized");
@@ -150,6 +196,33 @@ static void apic_set_task_priority(uint8_t priority)
apic_write32(APIC_TASK_PRIORITY, priority);
}
+static void apic_init_timer(void)
+{
+ die_if(!apic_bar, "APIC is not initialized");
+
+ apic_write32(APIC_LVT_TIMER, APIC_MASKED_BIT);
+
+ /* Divide the clock by 1. */
+ apic_write32(APIC_TIMER_DIV_CFG, 0xB);
+
+ /* Calibrate the APIC timer */
+ if (!ticks_per_ms) {
+ /* Set APIC init counter to MAX and count for 1 ms */
+ apic_write32(APIC_TIMER_INIT_COUNT, UINT32_MAX);
+
+ mdelay(1);
+
+ ticks_per_ms =
+ UINT32_MAX - apic_read32(APIC_TIMER_CUR_COUNT);
+ }
+
+ /* Clear the count so we don't get any stale interrupts */
+ apic_write32(APIC_TIMER_INIT_COUNT, 0);
+
+ /* Unmask the timer and set the vector. */
+ apic_write32(APIC_LVT_TIMER, APIC_TIMER_VECTOR);
+}
+
static void apic_sw_enable(void)
{
uint32_t reg = apic_read32(APIC_SPURIOUS);
@@ -183,6 +256,10 @@ void apic_init(void)
apic_sw_enable();
+ apic_init_timer();
+
+ set_interrupt_handler(APIC_TIMER_VECTOR, &timer_interrupt_handler);
+
_apic_initialized = 1;
printf("APIC Configured\n");