Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:10

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /**
0004  * @file
0005  *
0006  * @ingroup RTEMSBSPsX8664AMD64
0007  *
0008  * @brief APIC definitions
0009  */
0010 
0011 /*
0012  * Copyright (C) 2024 Matheus Pecoraro
0013  * Copyright (c) 2018 Amaan Cheval <amaan.cheval@gmail.com>
0014  *
0015  * Redistribution and use in source and binary forms, with or without
0016  * modification, are permitted provided that the following conditions
0017  * are met:
0018  * 1. Redistributions of source code must retain the above copyright
0019  *    notice, this list of conditions and the following disclaimer.
0020  * 2. Redistributions in binary form must reproduce the above copyright
0021  *    notice, this list of conditions and the following disclaimer in the
0022  *    documentation and/or other materials provided with the distribution.
0023  *
0024  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0025  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0027  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0028  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0029  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0030  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0031  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0032  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0033  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0034  * SUCH DAMAGE.
0035  */
0036 
0037 #ifndef _AMD64_APIC_H
0038 #define _AMD64_APIC_H
0039 
0040 #include <rtems/score/basedefs.h>
0041 
0042 #ifdef __cplusplus
0043 extern "C" {
0044 #endif
0045 
0046 /* The address of the MSR pointing to the APIC base physical address */
0047 #define APIC_BASE_MSR             0x1B
0048 /* Value to hardware-enable the APIC through the APIC_BASE_MSR */
0049 #define APIC_BASE_MSR_ENABLE      0x800
0050 
0051 #define xAPIC_MAX_APIC_ID         0xFE
0052 
0053 /*
0054  * Since the LAPIC registers are contained in an array of 32-bit elements
0055  * these byte-offsets need to be divided by 4 to index the array.
0056  */
0057 #define LAPIC_OFFSET(val) (val >> 2)
0058 
0059 #define LAPIC_REGISTER_ID            LAPIC_OFFSET(0x20)
0060 #define LAPIC_REGISTER_EOI           LAPIC_OFFSET(0x0B0)
0061 #define LAPIC_REGISTER_SPURIOUS      LAPIC_OFFSET(0x0F0)
0062 #define LAPIC_REGISTER_ICR_LOW       LAPIC_OFFSET(0x300)
0063 #define LAPIC_REGISTER_ICR_HIGH      LAPIC_OFFSET(0x310)
0064 #define LAPIC_REGISTER_LVT_TIMER     LAPIC_OFFSET(0x320)
0065 #define LAPIC_REGISTER_TIMER_INITCNT LAPIC_OFFSET(0x380)
0066 #define LAPIC_REGISTER_TIMER_CURRCNT LAPIC_OFFSET(0x390)
0067 #define LAPIC_REGISTER_TIMER_DIV     LAPIC_OFFSET(0x3E0)
0068 
0069 #define LAPIC_LVT_MASK               0x10000
0070 
0071 #define LAPIC_ICR_HIGH_MASK          0x00FFFFFF
0072 #define LAPIC_ICR_LOW_MASK           0xFFF32000
0073 #define LAPIC_ICR_DELIV_INIT         0x500
0074 #define LAPIC_ICR_DELIV_START        0x600
0075 #define LAPIC_ICR_DELIV_STAT_PEND    0x1000
0076 #define LAPIC_ICR_ASSERT             0x4000
0077 #define LAPIC_ICR_TRIG_LEVEL         0x8000
0078 
0079 #define LAPIC_EOI_ACK                0
0080 #define LAPIC_SELECT_TMR_PERIODIC    0x20000
0081 #define LAPIC_SPURIOUS_ENABLE        0x100
0082 
0083 /* Number of times to calibrate the LAPIC timer to average it out */
0084 #define LAPIC_TIMER_NUM_CALIBRATIONS 5
0085 /* Default divide value used by LAPIC timer */
0086 #define LAPIC_TIMER_DIVIDE_VALUE     16
0087 /* Value to set in register to pick the divide value above */
0088 #define LAPIC_TIMER_SELECT_DIVIDER   3
0089 
0090 /* PIT defines and macros used when calibrating the LAPIC timer and starting APs */
0091 #define PIT_FREQUENCY               1193180
0092 /*
0093  * The PIT_FREQUENCY determines how many times the PIT counter is decremented
0094  * per second - therefore, we can calculate how many ticks we set based on what
0095  * fraction of a second we're okay with spending on calibration
0096  */
0097 #define PIT_CALIBRATE_DIVIDER       20
0098 #define PIT_CALIBRATE_TICKS       (PIT_FREQUENCY/PIT_CALIBRATE_DIVIDER)
0099 /*
0100  * Since the PIT only has 2 one-byte registers, the maximum tick value is
0101  * limited to 16-bits. We can set the PIT to use a frequency divider if
0102  * needed.
0103  */
0104 RTEMS_STATIC_ASSERT(
0105   PIT_CALIBRATE_TICKS <= 0xffff,
0106   PIT_CALIBRATE_DIVIDER
0107 );
0108 
0109 /* I/O ports for the PIT */
0110 #define PIT_PORT_CHAN0              0x40
0111 #define PIT_PORT_CHAN1              0x41
0112 #define PIT_PORT_CHAN2              0x42
0113 /*
0114  * The input to channel 2 can be gated through software, using bit 0 of port
0115  * 0x61.
0116  */
0117 #define PIT_PORT_CHAN2_GATE         0x61
0118 #define PIT_CHAN2_TIMER_BIT         1
0119 #define PIT_CHAN2_SPEAKER_BIT       2
0120 /* The PIT mode/command register */
0121 #define PIT_PORT_MCR                0x43
0122 
0123 /* PIT values to select channels, access, and operating modes */
0124 #define PIT_SELECT_CHAN0            0b00000000
0125 #define PIT_SELECT_CHAN1            0b01000000
0126 #define PIT_SELECT_CHAN2            0b10000000
0127 /*
0128  * In the lo/hi mode, the low-byte is sent to the data port, followed by the
0129  * high-byte; this makes it important that this be an atomic operation.
0130  */
0131 #define PIT_SELECT_ACCESS_LOHI      0b00110000
0132 #define PIT_SELECT_ONE_SHOT_MODE    0b00000010
0133 #define PIT_SELECT_BINARY_MODE      0
0134 
0135 #define PIT_CHAN2_ENABLE(chan2_value)                                    \
0136   /* Enable the channel 2 timer gate and disable the speaker output */   \
0137   chan2_value = (inport_byte(PIT_PORT_CHAN2_GATE) | PIT_CHAN2_TIMER_BIT) \
0138     & ~PIT_CHAN2_SPEAKER_BIT;                                            \
0139   outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);                        \
0140   /* Initialize PIT in one-shot mode on Channel 2 */                     \
0141   outport_byte(                                                          \
0142     PIT_PORT_MCR,                                                        \
0143     PIT_SELECT_CHAN2 | PIT_SELECT_ACCESS_LOHI |                          \
0144       PIT_SELECT_ONE_SHOT_MODE | PIT_SELECT_BINARY_MODE                  \
0145   );                                                                     \
0146 
0147 #define PIT_CHAN2_WRITE_TICKS(pit_ticks)                                 \
0148   /* Set PIT reload value */                                             \
0149   outport_byte(PIT_PORT_CHAN2, pit_ticks & 0xff);                        \
0150   stub_io_wait();                                                        \
0151   outport_byte(PIT_PORT_CHAN2, (pit_ticks >> 8) & 0xff);                 \
0152 
0153 #define PIT_CHAN2_START_DELAY(chan2_value)                               \
0154   /* Restart PIT by disabling the gated input and then re-enabling it */ \
0155   chan2_value &= ~PIT_CHAN2_TIMER_BIT;                                   \
0156   outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);                        \
0157   chan2_value |= PIT_CHAN2_TIMER_BIT;                                    \
0158   outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);                        \
0159 
0160 #define PIT_CHAN2_WAIT_DELAY(pit_ticks)                                  \
0161   do {                                                                   \
0162     uint32_t curr_ticks = pit_ticks;                                     \
0163     while ( curr_ticks <= pit_ticks ) {                                  \
0164       /* Send latch command to read multi-byte value atomically */       \
0165       outport_byte(PIT_PORT_MCR, PIT_SELECT_CHAN2);                      \
0166       curr_ticks = inport_byte(PIT_PORT_CHAN2);                          \
0167       curr_ticks |= inport_byte(PIT_PORT_CHAN2) << 8;                    \
0168     }                                                                    \
0169   } while(0);                                                            \
0170 
0171 extern volatile uint32_t* amd64_lapic_base;
0172 extern uint8_t amd64_lapic_to_cpu_map[xAPIC_MAX_APIC_ID + 1];
0173 
0174 /**
0175  * @brief Initializes the Local APIC by hardware and software enabling it.
0176  *
0177  * Initializes the Local APIC by hardware and software enabling it, and sets
0178  * up the amd64_lapic_base pointer that can be used as a 32-bit addressable array to
0179  * access Local APIC registers.
0180  *
0181  * @return true if successful.
0182  */
0183 bool lapic_initialize(void);
0184 
0185 /**
0186  * @brief Calculates the number of Local APIC timer ticks which can be used
0187  *        with lapic_timer_enable to set up a timer of given frequency.
0188  *
0189  * @param desired_freq_hz The frequency in Hz.
0190  *
0191  * @return The number of Local APIC timer ticks.
0192  */
0193 uint32_t lapic_timer_calc_ticks(uint64_t desired_freq_hz);
0194 
0195 /**
0196  * @brief Enables the Local APIC timer.
0197  *
0198  * @param reload_value Number of ticks per interrupt.
0199  */
0200 void lapic_timer_enable(uint32_t reload_value);
0201 
0202 #ifdef RTEMS_SMP
0203 /**
0204  * @brief Retrieves the number of available processors in the system
0205  *
0206  * @return Number of available processors
0207  */
0208 uint32_t lapic_get_num_of_procesors(void);
0209 
0210 /**
0211  * @brief Sends an interprocessor interrupt to a specified processor.
0212  *
0213  * @param target_cpu_index The processor index of the target processor.
0214  * @param isr_vector The vector of the interrupt being sent.
0215  */
0216 void lapic_send_ipi(uint32_t target_cpu_index, uint8_t isr_vector);
0217 
0218 /**
0219  * @brief Starts the Application Processor that corresponds to cpu_index.
0220  *
0221  * @param cpu_index The processor to be started.
0222  * @param page_vector The under 1MB 4KB page where the trampoline code is located.
0223  */
0224 void lapic_start_ap(uint32_t cpu_index, uint8_t page_vector);
0225 #endif
0226 
0227 /**
0228  * @brief Retrieves the Local APIC ID
0229  * @return Local APIC ID
0230  */
0231 uint8_t inline lapic_get_id(void)
0232 {
0233   /* ID stored in highest 8 bits */
0234   return amd64_lapic_base[LAPIC_REGISTER_ID]>>24;
0235 }
0236 
0237 /**
0238  * @brief Signals an end of interrupt to the Local APIC
0239  */
0240 void inline lapic_eoi(void)
0241 {
0242   amd64_lapic_base[LAPIC_REGISTER_EOI] = LAPIC_EOI_ACK;
0243 }
0244 
0245 #ifdef __cplusplus
0246 }
0247 #endif
0248 
0249 #endif /* _AMD64_APIC_H */