| /* |
| * 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> |
| #include <irq.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 _arch_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 _arch_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; |
| } |