2019-07-05 05:17:14 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2019 Intel Corporation
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2022-05-06 10:49:15 +02:00
|
|
|
#include <zephyr/kernel.h>
|
2019-09-30 19:28:36 +02:00
|
|
|
#include <ksched.h>
|
2022-05-06 10:49:15 +02:00
|
|
|
#include <zephyr/arch/cpu.h>
|
2019-07-05 05:17:14 +02:00
|
|
|
#include <kernel_arch_data.h>
|
2020-11-03 15:14:22 +01:00
|
|
|
#include <kernel_arch_func.h>
|
2022-05-06 10:49:15 +02:00
|
|
|
#include <zephyr/drivers/interrupt_controller/sysapic.h>
|
|
|
|
#include <zephyr/drivers/interrupt_controller/loapic.h>
|
|
|
|
#include <zephyr/irq.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
2023-05-15 15:50:28 +02:00
|
|
|
#include <zephyr/sys/iterable_sections.h>
|
2023-12-13 23:51:31 +01:00
|
|
|
|
2020-06-16 21:49:54 +02:00
|
|
|
|
2020-11-26 19:32:34 +01:00
|
|
|
LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
|
2019-07-05 05:17:14 +02:00
|
|
|
|
|
|
|
unsigned char _irq_to_interrupt_vector[CONFIG_MAX_IRQ_LINES];
|
|
|
|
#define NR_IRQ_VECTORS (IV_NR_VECTORS - IV_IRQS) /* # vectors free for IRQs */
|
|
|
|
|
2021-11-14 14:15:46 +01:00
|
|
|
void (*x86_irq_funcs[NR_IRQ_VECTORS])(const void *arg);
|
2020-07-10 10:57:23 +02:00
|
|
|
const void *x86_irq_args[NR_IRQ_VECTORS];
|
2019-07-05 05:17:14 +02:00
|
|
|
|
2021-01-06 10:51:02 +01:00
|
|
|
#if defined(CONFIG_INTEL_VTD_ICTL)
|
|
|
|
|
2022-05-06 10:49:15 +02:00
|
|
|
#include <zephyr/device.h>
|
|
|
|
#include <zephyr/drivers/interrupt_controller/intel_vtd.h>
|
2021-01-06 10:51:02 +01:00
|
|
|
|
2022-08-17 15:37:22 +02:00
|
|
|
static const struct device *const vtd = DEVICE_DT_GET_ONE(intel_vt_d);
|
2021-01-06 10:51:02 +01:00
|
|
|
|
|
|
|
#endif /* CONFIG_INTEL_VTD_ICTL */
|
|
|
|
|
2020-07-10 10:57:23 +02:00
|
|
|
static void irq_spurious(const void *arg)
|
2020-06-16 21:49:54 +02:00
|
|
|
{
|
|
|
|
LOG_ERR("Spurious interrupt, vector %d\n", (uint32_t)(uint64_t)arg);
|
|
|
|
z_fatal_error(K_ERR_SPURIOUS_IRQ, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void x86_64_irq_init(void)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < NR_IRQ_VECTORS; i++) {
|
|
|
|
x86_irq_funcs[i] = irq_spurious;
|
2020-07-10 10:57:23 +02:00
|
|
|
x86_irq_args[i] = (const void *)(long)(i + IV_IRQS);
|
2020-06-16 21:49:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-03 15:14:22 +01:00
|
|
|
int z_x86_allocate_vector(unsigned int priority, int prev_vector)
|
2019-07-05 05:17:14 +02:00
|
|
|
{
|
|
|
|
const int VECTORS_PER_PRIORITY = 16;
|
|
|
|
const int MAX_PRIORITY = 13;
|
2020-11-03 15:14:22 +01:00
|
|
|
int vector = prev_vector;
|
2019-07-05 05:17:14 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (priority >= MAX_PRIORITY) {
|
|
|
|
priority = MAX_PRIORITY;
|
|
|
|
}
|
|
|
|
|
2020-11-03 15:14:22 +01:00
|
|
|
if (vector == -1) {
|
|
|
|
vector = (priority * VECTORS_PER_PRIORITY) + IV_IRQS;
|
|
|
|
}
|
2019-07-05 05:17:14 +02:00
|
|
|
|
|
|
|
for (i = 0; i < VECTORS_PER_PRIORITY; ++i, ++vector) {
|
2020-11-03 15:14:22 +01:00
|
|
|
if (prev_vector != 1 && vector == prev_vector) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-07-10 02:34:52 +02:00
|
|
|
#ifdef CONFIG_IRQ_OFFLOAD
|
|
|
|
if (vector == CONFIG_IRQ_OFFLOAD_VECTOR) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
2019-12-18 23:12:54 +01:00
|
|
|
if (vector == Z_X86_OOPS_VECTOR) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-11-03 15:14:22 +01:00
|
|
|
|
2020-06-16 21:49:54 +02:00
|
|
|
if (x86_irq_funcs[vector - IV_IRQS] == irq_spurious) {
|
2019-07-05 05:17:14 +02:00
|
|
|
return vector;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2020-11-13 09:27:28 +01:00
|
|
|
void z_x86_irq_connect_on_vector(unsigned int irq,
|
|
|
|
uint8_t vector,
|
|
|
|
void (*func)(const void *arg),
|
2021-03-18 14:29:40 +01:00
|
|
|
const void *arg)
|
2020-11-13 09:27:28 +01:00
|
|
|
{
|
|
|
|
_irq_to_interrupt_vector[irq] = vector;
|
|
|
|
x86_irq_funcs[vector - IV_IRQS] = func;
|
|
|
|
x86_irq_args[vector - IV_IRQS] = arg;
|
|
|
|
}
|
|
|
|
|
2019-07-05 05:17:14 +02:00
|
|
|
/*
|
|
|
|
* N.B.: the API docs don't say anything about returning error values, but
|
|
|
|
* this function returns -1 if a vector at the specific priority can't be
|
|
|
|
* allocated. Whether it should simply __ASSERT instead is up for debate.
|
|
|
|
*/
|
|
|
|
|
2019-11-07 21:43:29 +01:00
|
|
|
int arch_irq_connect_dynamic(unsigned int irq, unsigned int priority,
|
2024-06-25 08:49:10 +02:00
|
|
|
void (*routine)(const void *parameter),
|
|
|
|
const void *parameter, uint32_t flags)
|
2019-07-05 05:17:14 +02:00
|
|
|
{
|
2020-05-27 18:26:57 +02:00
|
|
|
uint32_t key;
|
2019-07-05 05:17:14 +02:00
|
|
|
int vector;
|
|
|
|
|
|
|
|
__ASSERT(irq <= CONFIG_MAX_IRQ_LINES, "IRQ %u out of range", irq);
|
|
|
|
|
|
|
|
key = irq_lock();
|
|
|
|
|
2020-11-03 15:14:22 +01:00
|
|
|
vector = z_x86_allocate_vector(priority, -1);
|
2019-07-05 05:17:14 +02:00
|
|
|
if (vector >= 0) {
|
2021-01-06 10:51:02 +01:00
|
|
|
#if defined(CONFIG_INTEL_VTD_ICTL)
|
2022-03-09 20:20:13 +01:00
|
|
|
if (device_is_ready(vtd)) {
|
2021-01-06 10:51:02 +01:00
|
|
|
int irte = vtd_allocate_entries(vtd, 1);
|
|
|
|
|
|
|
|
__ASSERT(irte >= 0, "IRTE allocation must succeed");
|
|
|
|
|
|
|
|
vtd_set_irte_vector(vtd, irte, vector);
|
|
|
|
vtd_set_irte_irq(vtd, irte, irq);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_INTEL_VTD_ICTL */
|
|
|
|
|
2021-03-18 14:29:40 +01:00
|
|
|
z_irq_controller_irq_config(vector, irq, flags);
|
2024-06-25 08:49:10 +02:00
|
|
|
z_x86_irq_connect_on_vector(irq, vector, routine, parameter);
|
2019-07-05 05:17:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
irq_unlock(key);
|
|
|
|
return vector;
|
|
|
|
}
|
2019-07-10 02:34:52 +02:00
|
|
|
|
2021-01-06 15:01:03 +01:00
|
|
|
|
|
|
|
/* The first bit is used to indicate whether the list of reserved interrupts
|
|
|
|
* have been initialized based on content stored in the irq_alloc linker
|
|
|
|
* section in ROM.
|
|
|
|
*/
|
|
|
|
#define IRQ_LIST_INITIALIZED 0
|
|
|
|
|
|
|
|
static ATOMIC_DEFINE(irq_reserved, CONFIG_MAX_IRQ_LINES);
|
|
|
|
|
|
|
|
static void irq_init(void)
|
|
|
|
{
|
2023-04-18 22:48:56 +02:00
|
|
|
TYPE_SECTION_FOREACH(const uint8_t, irq_alloc, irq) {
|
2021-01-06 15:01:03 +01:00
|
|
|
__ASSERT_NO_MSG(*irq < CONFIG_MAX_IRQ_LINES);
|
|
|
|
atomic_set_bit(irq_reserved, *irq);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int arch_irq_allocate(void)
|
|
|
|
{
|
|
|
|
unsigned int key = irq_lock();
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!atomic_test_and_set_bit(irq_reserved, IRQ_LIST_INITIALIZED)) {
|
|
|
|
irq_init();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(irq_reserved); i++) {
|
|
|
|
unsigned int fz, irq;
|
|
|
|
|
|
|
|
while ((fz = find_lsb_set(~atomic_get(&irq_reserved[i])))) {
|
|
|
|
irq = (fz - 1) + (i * sizeof(atomic_val_t) * 8);
|
|
|
|
if (irq >= CONFIG_MAX_IRQ_LINES) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!atomic_test_and_set_bit(irq_reserved, irq)) {
|
|
|
|
irq_unlock(key);
|
|
|
|
return irq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
irq_unlock(key);
|
|
|
|
|
|
|
|
return UINT_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
void arch_irq_set_used(unsigned int irq)
|
|
|
|
{
|
|
|
|
unsigned int key = irq_lock();
|
|
|
|
|
|
|
|
atomic_set_bit(irq_reserved, irq);
|
|
|
|
|
|
|
|
irq_unlock(key);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool arch_irq_is_used(unsigned int irq)
|
|
|
|
{
|
|
|
|
return atomic_test_bit(irq_reserved, irq);
|
|
|
|
}
|