blob: 89e0a0ad6e6111dcb7257f290ca5070f66d5af89 [file] [log] [blame]
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07001/*
2 * Copyright (c) 1984-2008, 2011-2015 Wind River Systems, Inc.
3 *
David B. Kinderac74d8b2017-01-18 17:01:01 -08004 * SPDX-License-Identifier: Apache-2.0
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -07005 */
6
Anas Nashif275ca602015-12-04 10:09:39 -05007/**
8 * @file
9 * @brief LoApicIntr.c - Intel Pentium[234] Local APIC/xAPIC driver
10 *
Dan Kalowskyda67b292015-10-20 09:42:33 -070011 * This module is a driver for the local APIC/xAPIC (Advanced Programmable
12 * Interrupt Controller) in P6 (PentiumPro, II, III) family processors
13 * and P7 (Pentium4) family processors. The local APIC/xAPIC is included
14 * in selected P6 (PentiumPro, II, III) and P7 (Pentium4) family processors.
15 * Beginning with the P6 family processors, the presence or absence of an
16 * on-chip local APIC can be detected using the CPUID instruction. When the
17 * CPUID instruction is executed, bit 9 of the feature flags returned in the
18 * EDX register indicates the presence (set) or absence (clear) of an on-chip
19 * local APIC.
20 *
21 * The local APIC performs two main functions for the processor:
22 * - It processes local external interrupts that the processor receives at its
23 * interrupt pins and local internal interrupts that software generates.
24 * - It communicates with an external IO APIC
25 * chip. The external IO APIC receives external interrupt events from
26 * peripheral and direct them to the local APIC. The IO APIC is
27 * part of Intel's system chip set.
28 * The local APIC controls the dispatching of interrupts (to its associated
29 * processor) that it receives either locally or from the IO APIC. It provides
30 * facilities for queuing, nesting and masking of interrupts. It handles the
31 * interrupt delivery protocol with its local processor and accesses to APIC
32 * registers.
33 * A timer on the local APIC allows local generation of interrupts, and
34 * local interrupt pins permit local reception of processor-specific interrupts.
35 * The local APIC can be disabled and used in conjunction with a standard 8259A
36 * style interrupt controller. Disabling the local APIC can be done in hardware
37 * for the Pentium processors or in software for the P6 and P7 (Pentium4) family
38 * processors.
39 *
40 * The local APIC in the Pentium4 processors (called the xAPIC) is an extension
41 * of the local APIC found in the P6 family processors. The primary difference
42 * between the APIC architecture and xAPIC architecture is that with Pentium4
43 * processors, the local xAPICs and IO xAPIC communicate with one another
44 * through the processors system bus; whereas, with the P6 family processors,
45 * communication between the local APICs and the IO APIC is handled through a
46 * dedicated 3-wire APIC bus. Also, some of the architectural features of the
47 * local APIC have been extended and/or modified in the local xAPIC.
48 *
49 * This driver contains three routines for use. They are:
Patrik Flykt97b3bd12019-03-12 15:15:42 -060050 * z_loapic_init() initializes the Local APIC for the interrupt mode chosen.
Dan Kalowskyda67b292015-10-20 09:42:33 -070051 * _loapic_enable()/disable() enables / disables the Local APIC.
52 *
53 * Local APIC is used in the Virtual Wire Mode: delivery mode ExtINT.
54 *
55 * Virtual Wire Mode is one of three interrupt modes defined by the MP
56 * specification. In this mode, interrupts are generated by the 8259A
57 * equivalent PICs (if present) and delivered to the Boot Strap Processor by
58 * the local APIC that is programmed to act as a "virtual Wire"; that
59 * is, the local APIC is logically indistinguishable from a hardware
60 * connection. This is a uniprocessor compatibility mode.
61 *
62 * The local and IO APICs support interrupts in the range of 32 to 255.
63 * Interrupt priority is implied by its vector, according to the following
64 * relationship: "priority = vector / 16".
65 * Here the quotient is rounded down to the nearest integer value to determine
66 * the priority, with 1 being the lowest and 15 is the highest. Because vectors
67 * 0 through 31 are reserved for exclusive use by the processor, the priority of
68 * user defined interrupts range from 2 to 15. A value of 15 in the Interrupt
69 * Class field of the Task Priority Register (TPR) will mask off all interrupts,
70 * which require interrupt service.
71 * The P6 family processor's local APIC includes an in-service entry and a
72 * holding entry for each priority level. To avoid losing interrupts, software
73 * should allocate no more than 2 interrupt vectors per priority. The Pentium4
74 * processor expands this support of all acceptance of two interrupts per vector
75 * rather than per priority level.
Dan Kalowskye8563c22015-10-20 09:42:33 -070076 *
Dan Kalowskyda67b292015-10-20 09:42:33 -070077 * INCLUDE FILES: loapic.h
Anas Nashifea0d0b22015-07-01 17:22:39 -040078 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070079
Flavio Santesb04cdcd2016-12-04 14:59:37 -060080#include <kernel.h>
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -070081#include <kernel_structs.h>
Dan Kalowskyc02dd342015-05-28 10:56:47 -070082#include <arch/cpu.h>
Kumar Gala78908162017-04-19 10:32:08 -050083#include <zephyr/types.h>
Jithu Joseph09a0c2f2016-05-06 21:55:51 -070084#include <string.h>
Andrew Boiea69ea782016-07-12 11:42:18 -070085#include <misc/__assert.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070086
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070087#include <toolchain.h>
Anas Nashif397d29d2017-06-17 11:30:47 -040088#include <linker/sections.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070089#include <drivers/loapic.h> /* public API declarations */
Daniel Leung921ee032015-12-08 11:17:56 -080090#include <init.h>
Jithu Joseph09a0c2f2016-05-06 21:55:51 -070091#include <drivers/sysapic.h>
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070092
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070093/* IA32_APIC_BASE MSR Bits */
94
95#define LOAPIC_BASE_MASK 0xfffff000 /* LO APIC Base Addr mask */
96#define LOAPIC_GLOBAL_ENABLE 0x00000800 /* LO APIC Global Enable */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -070097
98/* Local APIC ID Register Bits */
99
100#define LOAPIC_ID_MASK 0x0f000000 /* LO APIC ID mask */
101
102/* Local APIC Version Register Bits */
103
104#define LOAPIC_VERSION_MASK 0x000000ff /* LO APIC Version mask */
105#define LOAPIC_MAXLVT_MASK 0x00ff0000 /* LO APIC Max LVT mask */
106#define LOAPIC_PENTIUM4 0x00000014 /* LO APIC in Pentium4 */
107#define LOAPIC_LVT_PENTIUM4 5 /* LO APIC LVT - Pentium4 */
108#define LOAPIC_LVT_P6 4 /* LO APIC LVT - P6 */
109#define LOAPIC_LVT_P5 3 /* LO APIC LVT - P5 */
110
111/* Local APIC Vector Table Bits */
112
113#define LOAPIC_VECTOR 0x000000ff /* vectorNo */
114#define LOAPIC_MODE 0x00000700 /* delivery mode */
115#define LOAPIC_FIXED 0x00000000 /* delivery mode: FIXED */
116#define LOAPIC_SMI 0x00000200 /* delivery mode: SMI */
117#define LOAPIC_NMI 0x00000400 /* delivery mode: NMI */
118#define LOAPIC_EXT 0x00000700 /* delivery mode: ExtINT */
119#define LOAPIC_IDLE 0x00000000 /* delivery status: Idle */
120#define LOAPIC_PEND 0x00001000 /* delivery status: Pend */
121#define LOAPIC_HIGH 0x00000000 /* polarity: High */
122#define LOAPIC_LOW 0x00002000 /* polarity: Low */
123#define LOAPIC_REMOTE 0x00004000 /* remote IRR */
124#define LOAPIC_EDGE 0x00000000 /* trigger mode: Edge */
125#define LOAPIC_LEVEL 0x00008000 /* trigger mode: Level */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700126
127/* Local APIC Spurious-Interrupt Register Bits */
128
129#define LOAPIC_ENABLE 0x100 /* APIC Enabled */
130#define LOAPIC_FOCUS_DISABLE 0x200 /* Focus Processor Checking */
131
132/* Local Vector's lock-unlock macro used in loApicIntLock/Unlock */
133
134#define LOCKED_TIMER 0x01
135#define LOCKED_PMC 0x02
136#define LOCKED_LINT0 0x04
137#define LOCKED_LINT1 0x08
138#define LOCKED_ERROR 0x10
139#define LOCKED_THERMAL 0x20
140
141/* Interrupt Command Register: delivery mode and status */
142
143#define MODE_FIXED 0x0 /* delivery mode: Fixed */
144#define MODE_LOWEST 0x1 /* delivery mode: Lowest */
145#define MODE_SMI 0x2 /* delivery mode: SMI */
146#define MODE_NMI 0x4 /* delivery mode: NMI */
147#define MODE_INIT 0x5 /* delivery mode: INIT */
148#define MODE_STARTUP 0x6 /* delivery mode: StartUp */
149#define STATUS_PEND 0x1000 /* delivery status: Pend */
150
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700151/* IMCR related bits */
152
153#define IMCR_ADRS 0x22 /* IMCR addr reg */
154#define IMCR_DATA 0x23 /* IMCR data reg */
155#define IMCR_REG_SEL 0x70 /* IMCR reg select */
156#define IMCR_IOAPIC_ON 0x01 /* IMCR IOAPIC route enable */
157#define IMCR_IOAPIC_OFF 0x00 /* IMCR IOAPIC route disable */
158
Andrew Boiec25d6c52015-12-09 14:53:41 -0800159#if CONFIG_LOAPIC_SPURIOUS_VECTOR_ID == -1
160#define LOAPIC_SPURIOUS_VECTOR_ID (CONFIG_IDT_NUM_VECTORS - 1)
161#else
162#define LOAPIC_SPURIOUS_VECTOR_ID CONFIG_LOAPIC_SPURIOUS_VECTOR_ID
163#endif
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700164
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700165#define LOPIC_SSPND_BITS_PER_IRQ 1 /* Just the one for enable disable*/
166#define LOPIC_SUSPEND_BITS_REQD (ROUND_UP((LOAPIC_IRQ_COUNT * LOPIC_SSPND_BITS_PER_IRQ), 32))
167#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
168#include <power.h>
Kumar Galaccad5bf2017-04-21 10:03:20 -0500169u32_t loapic_suspend_buf[LOPIC_SUSPEND_BITS_REQD / 32] = {0};
170static u32_t loapic_device_power_state = DEVICE_PM_ACTIVE_STATE;
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700171#endif
172
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700173static ALWAYS_INLINE u32_t LOAPIC_READ(mem_addr_t addr)
174{
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700175#ifndef CONFIG_JAILHOUSE_X2APIC
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700176 return sys_read32(CONFIG_LOAPIC_BASE_ADDRESS + addr);
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700177#else
178 return read_x2apic(addr >> 4);
179#endif
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700180}
181
182static ALWAYS_INLINE void LOAPIC_WRITE(mem_addr_t addr, u32_t data)
183{
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700184#ifndef CONFIG_JAILHOUSE_X2APIC
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700185 sys_write32(data, CONFIG_LOAPIC_BASE_ADDRESS + addr);
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700186#else
187 write_x2apic(addr >> 4, data);
188#endif
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700189}
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700190
Anas Nashifea0d0b22015-07-01 17:22:39 -0400191/**
192 *
Anas Nashiff367f072015-07-01 17:51:40 -0400193 * @brief Initialize the Local APIC or xAPIC
Anas Nashifea0d0b22015-07-01 17:22:39 -0400194 *
195 * This routine initializes Local APIC or xAPIC.
196 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400197 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400198 *
199 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700200
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600201static int loapic_init(struct device *unused)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700202{
Dirk Brandewiebe1b1a42015-09-15 09:17:38 -0700203 ARG_UNUSED(unused);
Kumar Galaccad5bf2017-04-21 10:03:20 -0500204 s32_t loApicMaxLvt; /* local APIC Max LVT */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700205
206 /* enable the Local APIC */
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700207 LOAPIC_WRITE(LOAPIC_SVR, LOAPIC_READ(LOAPIC_SVR) | LOAPIC_ENABLE);
208 loApicMaxLvt = (LOAPIC_READ(LOAPIC_VER) & LOAPIC_MAXLVT_MASK) >> 16;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700209
210 /* reset the DFR, TPR, TIMER_CONFIG, and TIMER_ICR */
211
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700212 /* Jailhouse does not allow writes to DFR in x2APIC mode */
213#ifndef CONFIG_JAILHOUSE_X2APIC
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700214 LOAPIC_WRITE(LOAPIC_DFR, 0xffffffff);
Gustavo Lima Chaves9bb07ff2017-10-11 14:17:12 -0700215#endif
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700216
217 LOAPIC_WRITE(LOAPIC_TPR, 0x0);
218 LOAPIC_WRITE(LOAPIC_TIMER_CONFIG, 0x0);
219 LOAPIC_WRITE(LOAPIC_TIMER_ICR, 0x0);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700220
221 /* program Local Vector Table for the Virtual Wire Mode */
222
Gustavo Lima Chaves97a87162017-10-11 14:13:00 -0700223 /* skip LINT0/LINT1 for Jailhouse guest case, because we won't
224 * ever be waiting for interrupts on those
225 */
226#ifndef CONFIG_JAILHOUSE
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700227 /* set LINT0: extInt, high-polarity, edge-trigger, not-masked */
228
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700229 LOAPIC_WRITE(LOAPIC_LINT0, (LOAPIC_READ(LOAPIC_LINT0) &
230 ~(LOAPIC_MODE | LOAPIC_LOW |
231 LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
232 (LOAPIC_EXT | LOAPIC_HIGH | LOAPIC_EDGE));
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700233
234 /* set LINT1: NMI, high-polarity, edge-trigger, not-masked */
235
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700236 LOAPIC_WRITE(LOAPIC_LINT1, (LOAPIC_READ(LOAPIC_LINT1) &
237 ~(LOAPIC_MODE | LOAPIC_LOW |
238 LOAPIC_LEVEL | LOAPIC_LVT_MASKED)) |
239 (LOAPIC_NMI | LOAPIC_HIGH | LOAPIC_EDGE));
Gustavo Lima Chaves97a87162017-10-11 14:13:00 -0700240#endif
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700241
242 /* lock the Local APIC interrupts */
243
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700244 LOAPIC_WRITE(LOAPIC_TIMER, LOAPIC_LVT_MASKED);
245 LOAPIC_WRITE(LOAPIC_ERROR, LOAPIC_LVT_MASKED);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700246
Anas Nashif4c322582019-06-04 10:52:23 -0400247 if (loApicMaxLvt >= LOAPIC_LVT_P6) {
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700248 LOAPIC_WRITE(LOAPIC_PMC, LOAPIC_LVT_MASKED);
Anas Nashif4c322582019-06-04 10:52:23 -0400249 }
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700250
Anas Nashif4c322582019-06-04 10:52:23 -0400251 if (loApicMaxLvt >= LOAPIC_LVT_PENTIUM4) {
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700252 LOAPIC_WRITE(LOAPIC_THERMAL, LOAPIC_LVT_MASKED);
Anas Nashif4c322582019-06-04 10:52:23 -0400253 }
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700254
Andrew Boiec25d6c52015-12-09 14:53:41 -0800255#if CONFIG_LOAPIC_SPURIOUS_VECTOR
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700256 LOAPIC_WRITE(LOAPIC_SVR, (LOAPIC_READ(LOAPIC_SVR) & 0xFFFFFF00) |
257 (LOAPIC_SPURIOUS_VECTOR_ID & 0xFF));
Andrew Boiec25d6c52015-12-09 14:53:41 -0800258#endif
259
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700260 /* discard a pending interrupt if any */
Andrew Boiee98ac232016-08-02 12:05:08 -0700261#if CONFIG_EOI_FORWARDING_BUG
Patrik Flykt4344e272019-03-08 14:19:05 -0700262 z_lakemont_eoi();
Andrew Boiee98ac232016-08-02 12:05:08 -0700263#else
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700264 LOAPIC_WRITE(LOAPIC_EOI, 0);
Andrew Boiee98ac232016-08-02 12:05:08 -0700265#endif
266
Dirk Brandewiebe1b1a42015-09-15 09:17:38 -0700267 return 0;
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700268}
269
Anas Nashifea0d0b22015-07-01 17:22:39 -0400270/**
271 *
Anas Nashiff367f072015-07-01 17:51:40 -0400272 * @brief Set the vector field in the specified RTE
Anas Nashifea0d0b22015-07-01 17:22:39 -0400273 *
Andrew Boie325cae52016-09-22 11:20:26 -0700274 * This associates an IRQ with the desired vector in the IDT.
Anas Nashifea0d0b22015-07-01 17:22:39 -0400275 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400276 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400277 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700278
Patrik Flykt4344e272019-03-08 14:19:05 -0700279void z_loapic_int_vec_set(unsigned int irq, /* IRQ number of the interrupt */
Peter Mitsis2a4a6cf2015-07-27 11:02:41 -0400280 unsigned int vector /* vector to copy into the LVT */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700281 )
282{
Flavio Ceolin0866d182018-08-14 17:57:08 -0700283 unsigned int oldLevel; /* previous interrupt lock level */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700284
285 /*
286 * The following mappings are used:
287 *
288 * IRQ0 -> LOAPIC_TIMER
289 * IRQ1 -> LOAPIC_THERMAL
290 * IRQ2 -> LOAPIC_PMC
291 * IRQ3 -> LOAPIC_LINT0
292 * IRQ4 -> LOAPIC_LINT1
293 * IRQ5 -> LOAPIC_ERROR
294 *
295 * It's assumed that LVTs are spaced by 0x10 bytes
296 */
297
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700298 /* update the 'vector' bits in the LVT */
299
300 oldLevel = irq_lock();
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700301 LOAPIC_WRITE(LOAPIC_TIMER + (irq * 0x10),
302 (LOAPIC_READ(LOAPIC_TIMER + (irq * 0x10)) &
303 ~LOAPIC_VECTOR) | vector);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700304 irq_unlock(oldLevel);
305}
306
Anas Nashifea0d0b22015-07-01 17:22:39 -0400307/**
308 *
Anas Nashiff367f072015-07-01 17:51:40 -0400309 * @brief Enable an individual LOAPIC interrupt (IRQ)
Anas Nashifea0d0b22015-07-01 17:22:39 -0400310 *
Dan Kalowsky3a109b12015-10-20 09:42:33 -0700311 * @param irq the IRQ number of the interrupt
312 *
Anas Nashifea0d0b22015-07-01 17:22:39 -0400313 * This routine clears the interrupt mask bit in the LVT for the specified IRQ
314 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400315 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400316 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700317
Patrik Flykt4344e272019-03-08 14:19:05 -0700318void z_loapic_irq_enable(unsigned int irq)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700319{
Flavio Ceolin0866d182018-08-14 17:57:08 -0700320 unsigned int oldLevel; /* previous interrupt lock level */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700321
322 /*
323 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
324 * and ths assumption concerning LVT spacing.
325 */
326
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700327 /* clear the mask bit in the LVT */
328
329 oldLevel = irq_lock();
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700330 LOAPIC_WRITE(LOAPIC_TIMER + (irq * 0x10),
331 LOAPIC_READ(LOAPIC_TIMER + (irq * 0x10)) &
332 ~LOAPIC_LVT_MASKED);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700333 irq_unlock(oldLevel);
334}
335
Anas Nashifea0d0b22015-07-01 17:22:39 -0400336/**
337 *
Anas Nashiff367f072015-07-01 17:51:40 -0400338 * @brief Disable an individual LOAPIC interrupt (IRQ)
Anas Nashifea0d0b22015-07-01 17:22:39 -0400339 *
Dan Kalowsky3a109b12015-10-20 09:42:33 -0700340 * @param irq the IRQ number of the interrupt
341 *
Anas Nashifea0d0b22015-07-01 17:22:39 -0400342 * This routine clears the interrupt mask bit in the LVT for the specified IRQ
343 *
Anas Nashif1362e3c2015-07-01 17:29:04 -0400344 * @return N/A
Anas Nashifea0d0b22015-07-01 17:22:39 -0400345 */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700346
Patrik Flykt4344e272019-03-08 14:19:05 -0700347void z_loapic_irq_disable(unsigned int irq)
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700348{
Flavio Ceolin0866d182018-08-14 17:57:08 -0700349 unsigned int oldLevel; /* previous interrupt lock level */
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700350
351 /*
352 * See the comments in _LoApicLvtVecSet() regarding IRQ to LVT mappings
353 * and ths assumption concerning LVT spacing.
354 */
355
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700356 /* set the mask bit in the LVT */
357
358 oldLevel = irq_lock();
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700359 LOAPIC_WRITE(LOAPIC_TIMER + (irq * 0x10),
360 LOAPIC_READ(LOAPIC_TIMER + (irq * 0x10)) |
361 LOAPIC_LVT_MASKED);
Inaky Perez-Gonzalez8ddf82c2015-04-10 16:44:37 -0700362 irq_unlock(oldLevel);
363}
Yonattan Louised2108bf2015-08-21 19:10:32 -0500364
365
366/**
367 * @brief Find the currently executing interrupt vector, if any
368 *
369 * This routine finds the vector of the interrupt that is being processed.
370 * The ISR (In-Service Register) register contain the vectors of the interrupts
David B. Kinder896cf7a2017-04-19 10:45:34 -0700371 * in service. And the higher vector is the identification of the interrupt
Yonattan Louised2108bf2015-08-21 19:10:32 -0500372 * being currently processed.
373 *
Andrew Boiea69ea782016-07-12 11:42:18 -0700374 * This function must be called with interrupts locked in interrupt context.
375 *
Yonattan Louised2108bf2015-08-21 19:10:32 -0500376 * ISR registers' offsets:
377 * --------------------
378 * | Offset | bits |
379 * --------------------
380 * | 0100H | 0:31 |
381 * | 0110H | 32:63 |
382 * | 0120H | 64:95 |
383 * | 0130H | 96:127 |
384 * | 0140H | 128:159 |
385 * | 0150H | 160:191 |
386 * | 0160H | 192:223 |
387 * | 0170H | 224:255 |
388 * --------------------
389 *
Andrew Boieeec13ca2016-09-08 11:01:23 -0700390 * @return The vector of the interrupt that is currently being processed, or -1
391 * if no IRQ is being serviced.
Yonattan Louised2108bf2015-08-21 19:10:32 -0500392 */
Andrew Boiee98ac232016-08-02 12:05:08 -0700393int __irq_controller_isr_vector_get(void)
Yonattan Louised2108bf2015-08-21 19:10:32 -0500394{
Andrew Boiea69ea782016-07-12 11:42:18 -0700395 int pReg, block;
Yonattan Louised2108bf2015-08-21 19:10:32 -0500396
Andrew Boieeec13ca2016-09-08 11:01:23 -0700397 /* Block 0 bits never lit up as these are all exception or reserved
398 * vectors
399 */
400 for (block = 7; likely(block > 0); block--) {
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700401 pReg = LOAPIC_READ(LOAPIC_ISR + (block * 0x10));
Andrew Boiea69ea782016-07-12 11:42:18 -0700402 if (pReg) {
403 return (block * 32) + (find_msb_set(pReg) - 1);
Yonattan Louised2108bf2015-08-21 19:10:32 -0500404 }
Yonattan Louised2108bf2015-08-21 19:10:32 -0500405
Andrew Boiea69ea782016-07-12 11:42:18 -0700406 }
Andrew Boieeec13ca2016-09-08 11:01:23 -0700407 return -1;
Yonattan Louised2108bf2015-08-21 19:10:32 -0500408}
Daniel Leung921ee032015-12-08 11:17:56 -0800409
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700410#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
amirkaplc4902192016-09-11 19:17:19 +0300411static int loapic_suspend(struct device *port)
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700412{
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700413 volatile u32_t lvt; /* local vector table entry value */
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700414 int loapic_irq;
415
416 ARG_UNUSED(port);
417
Flavio Ceolinda49f2e2018-09-11 19:09:03 -0700418 (void)memset(loapic_suspend_buf, 0, (LOPIC_SUSPEND_BITS_REQD >> 3));
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700419
420 for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
421
422 if (_irq_to_interrupt_vector[LOAPIC_IRQ_BASE + loapic_irq]) {
423
424 /* Since vector numbers are already present in RAM/ROM,
425 * We save only the mask bits here.
426 */
Gustavo Lima Chaves1a8e72c2017-10-11 14:08:17 -0700427 lvt = LOAPIC_READ(LOAPIC_TIMER + (loapic_irq * 0x10));
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700428
Patrik Flykt24d71432019-03-26 19:57:45 -0600429 if ((lvt & LOAPIC_LVT_MASKED) == 0U) {
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700430 sys_bitfield_set_bit((mem_addr_t)loapic_suspend_buf,
431 loapic_irq);
432 }
433 }
434 }
amirkaplc4902192016-09-11 19:17:19 +0300435 loapic_device_power_state = DEVICE_PM_SUSPEND_STATE;
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700436 return 0;
437}
438
amirkaplc4902192016-09-11 19:17:19 +0300439int loapic_resume(struct device *port)
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700440{
441 int loapic_irq;
442
443 ARG_UNUSED(port);
444
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700445 /* Assuming all loapic device registers lose their state, the call to
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600446 * z_loapic_init(), should bring all the registers to a sane state.
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700447 */
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600448 loapic_init(NULL);
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700449
450 for (loapic_irq = 0; loapic_irq < LOAPIC_IRQ_COUNT; loapic_irq++) {
451
452 if (_irq_to_interrupt_vector[LOAPIC_IRQ_BASE + loapic_irq]) {
453 /* Configure vector and enable the required ones*/
Patrik Flykt4344e272019-03-08 14:19:05 -0700454 z_loapic_int_vec_set(loapic_irq,
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700455 _irq_to_interrupt_vector[LOAPIC_IRQ_BASE + loapic_irq]);
456
457 if (sys_bitfield_test_bit((mem_addr_t) loapic_suspend_buf,
458 loapic_irq)) {
Patrik Flykt4344e272019-03-08 14:19:05 -0700459 z_loapic_irq_enable(loapic_irq);
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700460 }
461 }
462 }
amirkaplc4902192016-09-11 19:17:19 +0300463 loapic_device_power_state = DEVICE_PM_ACTIVE_STATE;
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700464
465 return 0;
466}
467
amirkaplc4902192016-09-11 19:17:19 +0300468/*
469* Implements the driver control management functionality
470* the *context may include IN data or/and OUT data
471*/
Kumar Galaccad5bf2017-04-21 10:03:20 -0500472static int loapic_device_ctrl(struct device *port, u32_t ctrl_command,
Ramakrishna Pallalae1639b52019-02-14 09:35:42 +0530473 void *context, device_pm_cb cb, void *arg)
amirkaplc4902192016-09-11 19:17:19 +0300474{
Ramakrishna Pallalae1639b52019-02-14 09:35:42 +0530475 int ret = 0;
476
amirkaplc4902192016-09-11 19:17:19 +0300477 if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
Kumar Galaccad5bf2017-04-21 10:03:20 -0500478 if (*((u32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
Ramakrishna Pallalae1639b52019-02-14 09:35:42 +0530479 ret = loapic_suspend(port);
Kumar Galaccad5bf2017-04-21 10:03:20 -0500480 } else if (*((u32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
Ramakrishna Pallalae1639b52019-02-14 09:35:42 +0530481 ret = loapic_resume(port);
amirkaplc4902192016-09-11 19:17:19 +0300482 }
483 } else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
Kumar Galaccad5bf2017-04-21 10:03:20 -0500484 *((u32_t *)context) = loapic_device_power_state;
amirkaplc4902192016-09-11 19:17:19 +0300485 }
486
Ramakrishna Pallalae1639b52019-02-14 09:35:42 +0530487 if (cb) {
488 cb(port, ret, context, arg);
489 }
490
491 return ret;
amirkaplc4902192016-09-11 19:17:19 +0300492}
493
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600494SYS_DEVICE_DEFINE("loapic", loapic_init, loapic_device_ctrl, PRE_KERNEL_1,
amirkaplc4902192016-09-11 19:17:19 +0300495 CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700496#else
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600497SYS_INIT(loapic_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
Jithu Joseph09a0c2f2016-05-06 21:55:51 -0700498#endif /* CONFIG_DEVICE_POWER_MANAGEMENT */
499
Andrew Boiec25d6c52015-12-09 14:53:41 -0800500
501#if CONFIG_LOAPIC_SPURIOUS_VECTOR
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600502extern void z_loapic_spurious_handler(void);
Andrew Boiec25d6c52015-12-09 14:53:41 -0800503
Patrik Flykt97b3bd12019-03-12 15:15:42 -0600504NANO_CPU_INT_REGISTER(z_loapic_spurious_handler, NANO_SOFT_IRQ,
Andrew Boiec25d6c52015-12-09 14:53:41 -0800505 LOAPIC_SPURIOUS_VECTOR_ID >> 4,
506 LOAPIC_SPURIOUS_VECTOR_ID, 0);
507#endif