summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/ish/registers.h3
-rw-r--r--core/minute-ia/interrupts.c35
-rw-r--r--core/minute-ia/irq_handler.h89
-rw-r--r--core/minute-ia/irq_handler_common.S68
-rw-r--r--core/minute-ia/switch.S81
-rw-r--r--core/minute-ia/task.c30
-rw-r--r--core/minute-ia/task_defs.h5
7 files changed, 179 insertions, 132 deletions
diff --git a/chip/ish/registers.h b/chip/ish/registers.h
index 592f09b991..03aac276fb 100644
--- a/chip/ish/registers.h
+++ b/chip/ish/registers.h
@@ -94,7 +94,8 @@ enum ish_i2c_port {
/* APIC interrupt vectors */
#define ISH_TS_VECTOR 0x20 /* Task switch vector */
-#define LAPIC_LVT_ERROR_VECTOR 0x21
+#define LAPIC_LVT_ERROR_VECTOR 0x21 /* Clears IOAPIC/LAPIC sync errors */
+#define SOFTIRQ_VECTOR 0x22 /* Handles software generated IRQs */
#define LAPIC_SPURIOUS_INT_VECTOR 0xff
/* Interrupt to vector mapping. To be programmed into IOAPIC */
diff --git a/core/minute-ia/interrupts.c b/core/minute-ia/interrupts.c
index 87926a9736..a08e02de90 100644
--- a/core/minute-ia/interrupts.c
+++ b/core/minute-ia/interrupts.c
@@ -349,6 +349,11 @@ __asm__ (
"movl %esp, %eax\n"
"movl $stack_end, %esp\n"
"push %eax\n"
+#ifdef CONFIG_TASK_PROFILING
+ "push $" STRINGIFY(CONFIG_IRQ_COUNT) "\n"
+ "call task_start_irq_handler\n"
+ "addl $0x04, %esp\n"
+#endif
"call handle_lapic_lvt_error\n"
"pop %esp\n"
"movl $0x00, (0xFEE000B0)\n" /* Set EOI for LAPIC */
@@ -369,6 +374,30 @@ void unhandled_vector(void)
/* This needs to be moved to link_defs.h */
extern const struct irq_data __irq_data[], __irq_data_end[];
+/**
+ * Called from SOFTIRQ_VECTOR when software is trigger an IRQ manually
+ *
+ * If IRQ is out of range, then no routine should be called
+ */
+void call_irq_service_routine(uint32_t irq)
+{
+ const struct irq_data *p = __irq_data;
+
+ /* If just rescheduling a task, we won't have a routine to call */
+ if (irq >= CONFIG_IRQ_COUNT)
+ return;
+
+ for (; p < __irq_data_end; p++) {
+ if (p->irq == irq) {
+ p->routine();
+ break;
+ }
+ }
+
+ if (p == __irq_data_end)
+ CPRINTS("IRQ %d routine not found!", irq);
+}
+
void init_interrupts(void)
{
unsigned entry;
@@ -378,7 +407,11 @@ void init_interrupts(void)
/* Setup gates for IRQs declared by drivers using DECLARE_IRQ */
for (; p < __irq_data_end; p++)
- set_interrupt_gate(IRQ_TO_VEC(p->irq), p->routine, IDT_DESC_FLAGS);
+ set_interrupt_gate(IRQ_TO_VEC(p->irq), p->ioapic_routine,
+ IDT_DESC_FLAGS);
+
+ /* Software generated IRQ */
+ set_interrupt_gate(SOFTIRQ_VECTOR, sw_irq_handler, IDT_DESC_FLAGS);
/* Setup gate for LAPIC_LVT_ERROR vector; clear any remnant error. */
REG32(LAPIC_ESR_REG) = 0;
diff --git a/core/minute-ia/irq_handler.h b/core/minute-ia/irq_handler.h
index b691dc71fb..1d44a577bd 100644
--- a/core/minute-ia/irq_handler.h
+++ b/core/minute-ia/irq_handler.h
@@ -11,35 +11,11 @@
#include "registers.h"
#include "task_defs.h"
-#ifdef CONFIG_FPU
-#define save_fpu_ctx "movl "USE_FPU_OFFSET_STR"(%eax), %ebx\n" \
- "test %ebx, %ebx\n" \
- "jz 9f\n" \
- "fnsave "FPU_CTX_OFFSET_STR"(%eax)\n" \
- "9:\n"
-
-#define rstr_fpu_ctx "movl "USE_FPU_OFFSET_STR"(%eax), %ebx\n" \
- "test %ebx, %ebx\n" \
- "jz 9f\n" \
- "frstor "FPU_CTX_OFFSET_STR"(%eax)\n" \
- "9:\n"
-#else
-#define save_fpu_ctx
-#define rstr_fpu_ctx
-#endif
-
-#ifdef CONFIG_TASK_PROFILING
-#define task_start_irq_handler_call(vector) \
- "push $"#vector"\n" \
- "call task_start_irq_handler\n" \
- "addl $0x4, %esp\n"
-#else
-#define task_start_irq_handler_call(vector)
-#endif
-
+asm (".include \"core/minute-ia/irq_handler_common.S\"");
struct irq_data {
void (*routine)(void);
+ void (*ioapic_routine)(void);
int irq;
};
@@ -55,43 +31,28 @@ struct irq_data {
* Note: currently we don't allow nested irq handling
*/
#define DECLARE_IRQ(irq, routine) DECLARE_IRQ_(irq, routine, irq + 32 + 10)
-/* Each irq has a irq_data structure placed in .rodata.irqs section,
- * to be used for dynamically setting up interrupt gates */
-#define DECLARE_IRQ_(irq, routine, vector) \
- void __keep routine(void); \
- void IRQ_HANDLER(irq)(void); \
- __asm__ (".section .rodata.irqs\n"); \
- const struct irq_data __keep CONCAT4(__irq_, irq, _, routine) \
- __attribute__((section(".rodata.irqs")))= {IRQ_HANDLER(irq), irq};\
- __asm__ ( \
- ".section .text._irq_"#irq"_handler\n" \
- "_irq_"#irq"_handler:\n" \
- "pusha\n" \
- ASM_LOCK_PREFIX "addl $1, __in_isr\n" \
- "movl %esp, %eax\n" \
- "movl $stack_end, %esp\n" \
- "push %eax\n" \
- task_start_irq_handler_call(vector) \
- "call "#routine"\n" \
- "push $0\n" \
- "push $0\n" \
- "call switch_handler\n" \
- "addl $0x08, %esp\n" \
- "pop %esp\n" \
- "test %eax, %eax\n" \
- "je 1f\n" \
- "movl current_task, %eax\n" \
- save_fpu_ctx \
- "movl %esp, (%eax)\n" \
- "movl next_task, %eax\n" \
- "movl %eax, current_task\n" \
- "movl (%eax), %esp\n" \
- rstr_fpu_ctx \
- "1:\n" \
- "movl $"#vector ", (0xFEC00040)\n" \
- "movl $0x00, (0xFEE000B0)\n" \
- ASM_LOCK_PREFIX "subl $1, __in_isr\n" \
- "popa\n" \
- "iret\n" \
+/*
+ * Each irq has a irq_data structure placed in .rodata.irqs section,
+ * to be used for dynamically setting up interrupt gates
+ */
+#define DECLARE_IRQ_(irq, routine, vector) \
+ void __keep routine(void); \
+ void IRQ_HANDLER(irq)(void); \
+ __asm__ (".section .rodata.irqs\n"); \
+ const struct irq_data __keep CONCAT4(__irq_, irq, _, routine) \
+ __attribute__((section(".rodata.irqs"))) = { routine, \
+ IRQ_HANDLER(irq), \
+ irq}; \
+ __asm__ ( \
+ ".section .text._irq_"#irq"_handler\n" \
+ "_irq_"#irq"_handler:\n" \
+ "pusha\n" \
+ ASM_LOCK_PREFIX "addl $1, __in_isr\n" \
+ "irq_handler_common $0 $0 $"#irq"\n" \
+ "movl $"#vector ", " STRINGIFY(IOAPIC_EOI_REG) "\n" \
+ "movl $0x00, " STRINGIFY(LAPIC_EOI_REG) "\n" \
+ ASM_LOCK_PREFIX "subl $1, __in_isr\n" \
+ "popa\n" \
+ "iret\n" \
);
#endif /* __CROS_EC_IRQ_HANDLER_H */
diff --git a/core/minute-ia/irq_handler_common.S b/core/minute-ia/irq_handler_common.S
new file mode 100644
index 0000000000..e07cf26ce1
--- /dev/null
+++ b/core/minute-ia/irq_handler_common.S
@@ -0,0 +1,68 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * x86 task swtching and interrupt handling
+ */
+
+#include "config.h"
+#include "registers.h"
+#include "task_defs.h"
+
+# desched resched irq are all inputs and should be set to registers or immediate
+# values
+.macro irq_handler_common desched resched irq
+ # __schedule() copies 'resched' to %ecx and 'desched' to %edx before
+ movl %esp, %eax
+ movl $stack_end, %esp # use system stack
+ push %eax # push sp of preempted context
+
+ # Push resched and desched on stack to pass them as function parameters
+ # to switch_handler(desched, resched). After call, we clean up stack
+ # pointer. Note, we do this now before call_irq_service_routine has a
+ # chance to clobber these caller-saved registers.
+ push \resched
+ push \desched
+
+ push \irq
+#ifdef CONFIG_TASK_PROFILING
+ call task_start_irq_handler
+#endif
+ # Leave IRQ on stack for handler
+ call call_irq_service_routine
+ addl $0x04, %esp
+
+ # Call switch_handler(desched, resched).
+ call switch_handler # switch task if needed
+ addl $0x08, %esp
+
+ pop %esp # restore sp of preempted context
+
+ test %eax, %eax # Check if task switch required
+ jz 1f
+
+ movl current_task, %eax
+
+#ifdef CONFIG_FPU
+ movl USE_FPU_OFFSET(%eax), %ecx
+ test %ecx, %ecx
+ jz 2f
+ fnsave FPU_CTX_OFFSET(%eax) # Save current FPU context(current->fp_ctx)
+ 2:
+#endif
+
+ # Save SP of current task and switch to new task
+ movl %esp, (%eax)
+ movl next_task, %eax
+ movl %eax, current_task
+ movl (%eax), %esp
+
+#ifdef CONFIG_FPU
+ movl USE_FPU_OFFSET(%eax), %ecx
+ test %ecx, %ecx
+ jz 1f
+ frstor FPU_CTX_OFFSET(%eax) # Restore next FPU context
+#endif
+
+ 1:
+.endm
diff --git a/core/minute-ia/switch.S b/core/minute-ia/switch.S
index 1fdcc61bb8..c5098b9003 100644
--- a/core/minute-ia/switch.S
+++ b/core/minute-ia/switch.S
@@ -9,6 +9,8 @@
#include "registers.h"
#include "task_defs.h"
+#include "irq_handler_common.S"
+
.text
.extern current_task
@@ -17,6 +19,7 @@
.global __task_start
.global __switchto
.global default_int_handler
+.global sw_irq_handler
# Start the task scheduling. Start current_task (hook_task)
# This function is not an ISR but imitates the sequence.
@@ -67,72 +70,43 @@ default_int_handler:
ASM_LOCK_PREFIX subl $1, __in_isr
popa
iret
+.endfunc
+
+.align 4
+.func sw_irq_handler
+sw_irq_handler:
+ pusha
+ ASM_LOCK_PREFIX addl $1, __in_isr
+
+ # Call sw irq handler with irq number(%ecx) from task_trigger_irq.
+ # Pass 0 for both desched and resched since we don't need to deschedule
+ # our current task (and idle task can always be rescheduled)
+ irq_handler_common $0 $0 %ecx
+
+ # Indicate completion of servicing the interrupt to LAPIC.
+ # No IOAPIC EOI needed as this is SW triggered.
+ movl $0x00, LAPIC_EOI_REG
+ # Decrement ISR counter and restore general purpose registers.
+ ASM_LOCK_PREFIX subl $1, __in_isr
+ popa
+ iret
.endfunc
+
# Switches from one task to another if ready.
# __schedule triggers software interrupt ISH_TS_VECTOR, which is handled by
# __switchto
.align 4
.func __switchto
__switchto:
-
- # Save current task
pusha
ASM_LOCK_PREFIX addl $1, __in_isr
- movl %esp, %eax
- movl $stack_end, %esp # use system stack
- push %eax # push sp of preempted context
-
# __schedule() copies 'resched' to %ecx and 'desched' to %edx before
- # triggering ISH_TS_VECTOR
- #
- # Push %ecx and %edx into stack to pass them as function parameters
- # to switch_handler(desched, resched). After call, we clean up stack
- # pointer. Note, we do this now before task_start_irq has a chance
- # to clobber these caller-saved registers.
- push %ecx
- push %edx
-
-#ifdef CONFIG_TASK_PROFILING
- push $ISH_TS_VECTOR
- call task_start_irq_handler
- addl $0x4, %esp
-#endif
-
- # Stack is already set up from previous pushes
- call switch_handler
- addl $0x8, %esp # Clean up stack
- pop %esp # restore sp of preempted context
-
- test %eax, %eax # Check if task switch required
- jz 1f
-
- movl current_task, %eax
-
-#ifdef CONFIG_FPU
- movl USE_FPU_OFFSET(%eax), %ebx
- test %ebx, %ebx
- jz 2f
- fnsave FPU_CTX_OFFSET(%eax) # Save current FPU context(current->fp_ctx)
- 2:
-#endif
-
- # Save SP of current task and switch to new task
- movl %esp, (%eax)
- movl next_task, %eax
- movl %eax, current_task
- movl (%eax), %esp
-
-#ifdef CONFIG_FPU
- movl USE_FPU_OFFSET(%eax), %ebx
- test %ebx, %ebx
- jz 1f
- frstor FPU_CTX_OFFSET(%eax) # Restore next FPU context
-#endif
-
- 1:
+ # triggering ISH_TS_VECTOR.
+ # Call sw_irq with an invalid IRQ so it will skip calling a routine
+ irq_handler_common %edx %ecx $CONFIG_IRQ_COUNT
# Indicate completion of servicing the interrupt to LAPIC.
# No IOAPIC EOI needed as this is SW triggered.
@@ -142,5 +116,4 @@ __switchto:
ASM_LOCK_PREFIX subl $1, __in_isr
popa
iret
-
.endfunc
diff --git a/core/minute-ia/task.c b/core/minute-ia/task.c
index 8c3d9fd9f2..18c23f5515 100644
--- a/core/minute-ia/task.c
+++ b/core/minute-ia/task.c
@@ -261,11 +261,9 @@ uint32_t switch_handler(int desched, task_id_t resched)
void __schedule(int desched, int resched)
{
- __asm__ __volatile__ ("int %0"
- :
- : "i" (ISH_TS_VECTOR),
- "d" (desched), "c" (resched)
- );
+ __asm__ __volatile__("int %0"
+ :
+ : "i"(ISH_TS_VECTOR), "d"(desched), "c"(resched));
}
#ifdef CONFIG_TASK_PROFILING
@@ -276,15 +274,14 @@ void __keep task_start_irq_handler(void *data)
* pre-empted.
*/
uint32_t t = get_time().le.lo;
- uint32_t vector = (uint32_t)data;
- int irq = VEC_TO_IRQ(vector);
+ int irq = (uint32_t)data;
/*
* Track IRQ distribution. No need for atomic add, because an IRQ
* can't pre-empt itself. If less than 0, then the vector did not map
* to an IRQ but was for a synchronous exception instead (TS_VECTOR)
*/
- if (irq > 0 && irq < ARRAY_SIZE(irq_dist))
+ if (irq < CONFIG_IRQ_COUNT)
irq_dist[irq]++;
else
/* Track total number of service calls */
@@ -420,11 +417,20 @@ void task_clear_pending_irq(int irq)
void task_trigger_irq(int irq)
{
- /* Writing to Local APIC Interrupt Command Register (ICR) causes an
- * IPI (Inter-processor interrupt) on the APIC bus. Here we direct the
- * IPI to originating prccessor to generate self-interrupt
+ /* ISR should not be called before the first task is scheduled */
+ if (!task_start_called())
+ return;
+
+ /* we don't allow nested interrupt */
+ if (in_interrupt_context())
+ return;
+
+ /*
+ * "int" instruction accepts vector only as immediate value.
+ * so here, we use one vector(SOFTIRQ_VECTOR) and pass
+ * the address of ISR of irq in ecx register.
*/
- REG32(LAPIC_ICR_REG) = LAPIC_ICR_BITS | IRQ_TO_VEC(irq);
+ __asm__ __volatile__("int %0\n" : : "i"(SOFTIRQ_VECTOR), "c"(irq));
}
void mutex_lock(struct mutex *mtx)
diff --git a/core/minute-ia/task_defs.h b/core/minute-ia/task_defs.h
index 15aca6b8b5..01632392cb 100644
--- a/core/minute-ia/task_defs.h
+++ b/core/minute-ia/task_defs.h
@@ -19,6 +19,10 @@
#define USE_FPU_OFFSET_STR STRINGIFY(USE_FPU_OFFSET) /* "20" */
#define FPU_CTX_OFFSET_STR STRINGIFY(FPU_CTX_OFFSET) /* "24" */
+
+asm (".equ USE_FPU_OFFSET, "USE_FPU_OFFSET_STR);
+asm (".equ FPU_CTX_OFFSET, "FPU_CTX_OFFSET_STR);
+
#endif
#endif /* CONFIG_FPU */
@@ -42,6 +46,7 @@ typedef union {
int __task_start(int *start_called);
void __switchto(void);
+void sw_irq_handler(void);
/* Only the IF bit is set so tasks start with interrupts enabled. */
#define INITIAL_EFLAGS (0x200UL)