// SPDX-License-Identifier: MPL-2.0
#ifndef SH_LIB_LAPIC_H
#define SH_LIB_LAPIC_H
#include "std/status.h"
#include "std/type.h"
#include "memory/page.h"
#include "devs/devs.h"
#define SH_LAPIC_OFFSET_ID 0x020
#define SH_LAPIC_OFFSET_VERSION 0x030
#define SH_LAPIC_OFFSET_EOI 0x0B0
#define SH_LAPIC_OFFSET_SVR 0x0F0
#define SH_LAPIC_OFFSET_ESR 0x280
#define SH_LAPIC_OFFSET_ICR_LOW 0x300
#define SH_LAPIC_OFFSET_ICR_HIGH 0x310
#define SH_LAPIC_OFFSET_LVT_TIMER 0x320
#define SH_LAPIC_OFFSET_LVT_ERROR 0x370
#define SH_LAPIC_OFFSET_TIMER_INIT 0x380
#define SH_LAPIC_OFFSET_TIMER_CURR 0x390
#define SH_LAPIC_OFFSET_TIMER_DIV 0x3E0
#define SH_LAPIC_TIMER_DIVIDER 0x3
// LAPIC device structure
typedef struct {
  volatile sh_uint32 *base;
  sh_uint8 apic_id;
  sh_uint8 acpi_processor_id;
  sh_uint32 flags;
  sh_uint32 spurious_vector;
} sh_lapic_DEVICE;
// Initialize a LAPIC device structure, intented to initialize all LAPIC devices structures during MADT parsing
SH_STATUS sh_lapic_init(sh_uint64 lapic_phys,sh_uint8 spurious_vector,sh_uint8 apic_id,sh_uint8 apic_processor_id,sh_uint32 flags,sh_lapic_DEVICE *lapic);
// Initialize LAPIC for this CPU, intended to be used once by each CPU
SH_STATUS sh_lapic_init_dev(sh_uint8 spurious_vector,sh_lapic_DEVICE *lapic);
// Initialize DevS LAPIC backend (max_lapic_id is the max LAPIC id, not the number of LAPIC counted)
SH_STATUS sh_lapic_init_devs(sh_uint16 max_lapic_id,sh_uint16 max_acpi_processor_id);
// Bind a LAPIC device to DevS LAPIC backend
SH_STATUS sh_lapic_bind(sh_lapic_DEVICE *lapic_dev);
// Write 0 in provided LAPIC device EOI register
void sh_lapic_eoi(sh_lapic_DEVICE *lapic);
// Return a pointer to a LAPIC device based on the provided APIC id
sh_lapic_DEVICE *sh_lapic_get_dev_apic_id(sh_uint64 apic_id);
// Return a pointer to a LAPIC device based on the provided ACPI processor id
sh_lapic_DEVICE *sh_lapic_get_dev_acpi_cpu_id(sh_uint64 acpi_processor_id);
// Launch a LAPIC one-shot timer, independantely of the calibrated state of the LAPIC
SH_STATUS sh_lapic_timer_one_shot(sh_lapic_DEVICE *lapic_dev,sh_uint32 initial_value);
// Calibrate the LAPIC frequency
SH_STATUS sh_lapic_calibrate(sh_lapic_DEVICE *lapic_dev,sh_uint64 cpu_freq);
// Return the LAPIC frequency
sh_uint64 sh_lapic_get_frequency();
// Launch a LAPIC one-shot timer, LAPIC frequency need to be set
SH_STATUS sh_lapic_timer_one_shot_us(sh_lapic_DEVICE *lapic_dev,sh_uint64 microseconds_count);
// Return LAPIC DevS array
sh_lapic_DEVICE **sh_lapic_get_by_apic_id_array();
// Return max LAPIC APIC id
sh_uint64 sh_lapic_get_max_apic_id();
// Return max ACPI processor id
sh_uint64 sh_lapic_get_max_acpi_processor_id();
// Return LAPIC count
sh_uint64 sh_lapic_get_lapic_count();
#define SH_LAPIC_IPI_TYPE_NMI 0b100
#define SH_LAPIC_IPI_TYPE_INIT 0b101
#define SH_LAPIC_IPI_TYPE_STARTUP 0b110
#define SH_LAPIC_IPI_TYPE_FIXED 0b000
#define SH_LAPIC_IPI_DESTINATION_SPECIFIC (0b00<<18)
#define SH_LAPIC_IPI_DESTINATION_SELF (0b01<<18)
#define SH_LAPIC_IPI_DESTINATION_ALL (0b10<<18)
#define SH_LAPIC_IPI_DESTINATION_ALL_EXCLUDING_SELF (0b11<<18)
#define SH_LAPIC_IPI_NO_DESTINATION (sh_int16)(-1)
// Return SH_TRUE if LAPIC IPI feature is busy
sh_bool sh_lapic_ipi_is_busy(sh_lapic_DEVICE *lapic_dev);
// Return LAPIC id of current core, return -1 if error happened
sh_int16 sh_lapic_get_current_core();
// Send a fixed IPI with the specified vector. lapic_dev must be the struct of the current CPU.
// If destination_mode!=SH_LAPIC_IPI_DESTINATION_SPECIFIC, target_lapic_id must be SH_LAPIC_IPI_NO_DESTINATION
SH_STATUS sh_lapic_send_fixed_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint8 vector,sh_uint32 destination_mode,sh_int16 target_lapic_id);
// Send any IPI other than a fixed IPI to specified destination. lapic_dev must be the struct of the current CPU.
// ipi_type must be either SH_LAPIC_IPI_TYPE_INIT, SH_LAPIC_IPI_TYPE_STARTUP or SH_LAPIC_IPI_TYPE_NMI
// If ipi_type is SH_LAPIC_IPI_TYPE_STARTUP, start_address must contain the starting address. Address constraint (compatible with 16 bits mode) are checked.
// If ipi_type isn't SH_LAPIC_IPI_TYPE_STARTUP, start_address must be zero.
// If destination_mode!=SH_LAPIC_IPI_DESTINATION_SPECIFIC, target_lapic_id must be SH_LAPIC_IPI_NO_DESTINATION
SH_STATUS sh_lapic_send_ipi(sh_lapic_DEVICE *lapic_dev,sh_uint32 ipi_type,sh_uint32 destination_mode,sh_int16 target_lapic_id,sh_page_PHYSICAL_ADDRESS start_address);
// Parse a LAPIC DevS query
SH_STATUS sh_lapic_devs_query(char *sub_path,sh_devs_RESULT *result);
#endif
