zephyr/drivers/interrupt_controller/system_apic.c
Andrew Boie 9fc3afc339 x86: rebase priority levels
Having priority levels 0 and 1 reserved on x86 due to implementation
details on how the CPU uses the vector table is confusing to users,
and makes it unnecessarily difficult to share drivers between arches.

Now on x86, priority levels 0 and 1 are available. Semantically, all
priority levels have had 2 subtracted from them.

It is no longer necessary to specify a priority level when the
vector itself is specified. If an IDT entry has a specific vector
associated with it, any priority argument is simply ignored.

In gen_idt, some simplifications have been made:
- The printed representation of a generated entry now fits on one line
- Some checks being done in validate_priority() were redundant, as
  generate_interrupt_vector_bitmap() also ensures that there are
  sufficient free vectors within a priority level.

Change-Id: I26669d8ee0a53f48fbc2283490a8c42d8b1daf8e
Signed-off-by: Andrew Boie <andrew.p.boie@intel.com>
2016-02-08 21:45:07 -05:00

255 lines
6.7 KiB
C

/*
* Copyright (c) 2013-2015, Wind River Systems, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* @brief system module for variants with LOAPIC
*
*/
#include <misc/__assert.h>
#include "board.h"
#include <nanokernel.h>
#include <arch/cpu.h>
#include <drivers/ioapic.h>
#include <drivers/loapic.h>
#if !defined(LOAPIC_IRQ_BASE) && !defined (LOAPIC_IRQ_COUNT)
/* Default IA32 system APIC definitions with local APIC IRQs after IO APIC. */
#define LOAPIC_IRQ_BASE CONFIG_IOAPIC_NUM_RTES
#define LOAPIC_IRQ_COUNT 6 /* Default to LOAPIC_TIMER to LOAPIC_ERROR */
#define IS_IOAPIC_IRQ(irq) (irq < LOAPIC_IRQ_BASE)
#define HARDWARE_IRQ_LIMIT ((LOAPIC_IRQ_BASE + LOAPIC_IRQ_COUNT) - 1)
#else
/*
Assumption for boards that define LOAPIC_IRQ_BASE & LOAPIC_IRQ_COUNT that
local APIC IRQs are within IOAPIC RTE range.
*/
#define IS_IOAPIC_IRQ(irq) ((irq < LOAPIC_IRQ_BASE) || \
(irq >= (LOAPIC_IRQ_BASE + LOAPIC_IRQ_COUNT)))
#define HARDWARE_IRQ_LIMIT (CONFIG_IOAPIC_NUM_RTES - 1)
#endif
/* forward declarations */
static int __LocalIntVecAlloc(unsigned int irq, unsigned int priority);
/**
*
* @brief Allocate interrupt vector
*
* This routine is used by the x86's irq_connect_dynamic(). It performs the
* following functions:
*
* a) Allocates a vector satisfying the requested priority. The utility
* routine _IntVecAlloc() provided by the nanokernel will be used to
* perform the the allocation since the local APIC prioritizes interrupts
* as assumed by _IntVecAlloc().
* b) If an interrupt vector can be allocated, the IOAPIC redirection table
* (RED) or the LOAPIC local vector table (LVT) will be updated with the
* allocated interrupt vector.
*
* The board virtualizes IRQs as follows:
*
* - The first CONFIG_IOAPIC_NUM_RTES IRQs are provided by the IOAPIC
* - The remaining IRQs are provided by the LOAPIC.
*
* Thus, for example, if the IOAPIC supports 24 IRQs:
*
* - IRQ0 to IRQ23 map to IOAPIC IRQ0 to IRQ23
* - IRQ24 to IRQ29 map to LOAPIC LVT entries as follows:
*
* IRQ24 -> LOAPIC_TIMER
* IRQ25 -> LOAPIC_THERMAL
* IRQ26 -> LOAPIC_PMC
* IRQ27 -> LOAPIC_LINT0
* IRQ28 -> LOAPIC_LINT1
* IRQ29 -> LOAPIC_ERROR
*
* @param irq virtualized IRQ
* @param priority get vector from <priority> group
* @param flags Interrupt flags
*
* @return the allocated interrupt vector
*
* @internal
* For debug kernels, this routine will return -1 if there are no vectors
* remaining in the specified <priority> level, or if the <priority> or <irq>
* parameters are invalid.
* @endinternal
*/
int _SysIntVecAlloc(
unsigned int irq, /* virtualized IRQ */
unsigned int priority, /* get vector from <priority> group */
uint32_t flags /* interrupt flags */
)
{
int vector;
__ASSERT(priority < 14, "invalid priority");
__ASSERT(irq >= 0 && irq <= HARDWARE_IRQ_LIMIT, "invalid irq line");
vector = __LocalIntVecAlloc(irq, priority);
/*
* Set up the appropriate interrupt controller to generate the allocated
* interrupt vector for the specified IRQ
*/
__ASSERT(vector != -1, "bad vector id");
_SysIntVecProgram(vector, irq, flags);
return vector;
}
/**
*
* @brief Program interrupt controller
*
* This routine programs the interrupt controller with the given vector
* based on the given IRQ parameter.
*
* Drivers call this routine instead of IRQ_CONNECT() when interrupts are
* configured statically.
*
* The Galileo board virtualizes IRQs as follows:
*
* - The first CONFIG_IOAPIC_NUM_RTES IRQs are provided by the IOAPIC so the
* IOAPIC is programmed for these IRQs
* - The remaining IRQs are provided by the LOAPIC and hence the LOAPIC is
* programmed.
*
* @param vector the vector number
* @param irq the virtualized IRQ
* @param flags interrupt flags
*
*/
void _SysIntVecProgram(unsigned int vector, unsigned int irq, uint32_t flags)
{
if (IS_IOAPIC_IRQ(irq)) {
_ioapic_irq_set(irq, vector, flags);
} else {
_loapic_int_vec_set(irq - LOAPIC_IRQ_BASE, vector);
}
}
/**
*
* @brief Enable an individual interrupt (IRQ)
*
* The public interface for enabling/disabling a specific IRQ for the IA-32
* architecture is defined as follows in include/arch/x86/arch.h
*
* extern void irq_enable (unsigned int irq);
* extern void irq_disable (unsigned int irq);
*
* The irq_enable() routine is provided by the interrupt controller driver due
* to the IRQ virtualization that is performed by this platform. See the
* comments in _SysIntVecAlloc() for more information regarding IRQ
* virtualization.
*
* @return N/A
*/
void irq_enable(unsigned int irq)
{
if (IS_IOAPIC_IRQ(irq)) {
_ioapic_irq_enable(irq);
} else {
_loapic_irq_enable(irq - LOAPIC_IRQ_BASE);
}
}
/**
*
* @brief Disable an individual interrupt (IRQ)
*
* The irq_disable() routine is provided by the interrupt controller driver due
* to the IRQ virtualization that is performed by this platform. See the
* comments in _SysIntVecAlloc() for more information regarding IRQ
* virtualization.
*
* @return N/A
*/
void irq_disable(unsigned int irq)
{
if (IS_IOAPIC_IRQ(irq)) {
_ioapic_irq_disable(irq);
} else {
_loapic_irq_disable(irq - LOAPIC_IRQ_BASE);
}
}
#ifdef FIXED_HARDWARE_IRQ_TO_VEC_MAPPING
static inline int handle_fixed_mapping(int irq, int *vector)
{
/*
* On this board Hardware IRQs are fixed and not programmable.
*/
*vector = FIXED_HARDWARE_IRQ_TO_VEC_MAPPING(irq);
/* mark vector as allocated */
_IntVecMarkAllocated(*vector);
return *vector;
}
#else
static inline int handle_fixed_mapping(int irq, int *vector)
{
ARG_UNUSED(irq);
ARG_UNUSED(vector);
return 0;
}
#endif
/**
*
* @brief Local allocate interrupt vector.
*
* @returns The allocated interrupt vector
*
*/
static int __LocalIntVecAlloc(
unsigned int irq, /* virtualized IRQ */
unsigned int priority /* get vector from <priority> group */
)
{
int vector;
if (handle_fixed_mapping(irq, &vector)) {
return vector;
}
/*
* Use the nanokernel utility function _IntVecAlloc(). A value of
* -1 will be returned if there are no free vectors in the requested
* priority.
*/
vector = _IntVecAlloc(priority);
__ASSERT(vector != -1, "No free vectors in the requested priority");
return vector;
}