#ifndef _X86_APIC_H_
#define _X86_APIC_H_

#include <bitops.h>
#include <stdint.h>

#include "apic-defs.h"
#include "smp.h"

extern u8 id_map[MAX_TEST_CPUS];

typedef struct {
    uint8_t vector;
    uint8_t delivery_mode:3;
    uint8_t dest_mode:1;
    uint8_t delivery_status:1;
    uint8_t polarity:1;
    uint8_t remote_irr:1;
    uint8_t trig_mode:1;
    uint8_t mask:1;
    uint8_t reserve:7;
    uint8_t reserved[4];
    uint8_t dest_id;
} ioapic_redir_entry_t;

typedef enum trigger_mode {
	TRIGGER_EDGE = 0,
	TRIGGER_LEVEL,
	TRIGGER_MAX,
} trigger_mode_t;

void mask_pic_interrupts(void);

void eoi(void);
uint8_t apic_get_tpr(void);
void apic_set_tpr(uint8_t tpr);

void ioapic_write_redir(unsigned line, ioapic_redir_entry_t e);
void ioapic_write_reg(unsigned reg, uint32_t value);
ioapic_redir_entry_t ioapic_read_redir(unsigned line);
uint32_t ioapic_read_reg(unsigned reg);

void ioapic_set_redir(unsigned line, unsigned vec,
		trigger_mode_t trig_mode);

void set_mask(unsigned line, int mask);

void set_irq_line(unsigned line, int val);

void enable_apic(void);
uint32_t apic_read(unsigned reg);
bool apic_read_bit(unsigned reg, int n);
void apic_write(unsigned reg, uint32_t val);
void apic_icr_write(uint32_t val, uint32_t dest);
uint32_t apic_id(void);
uint32_t pre_boot_apic_id(void);


int enable_x2apic(void);
void disable_apic(void);
void reset_apic(void);
void init_apic_map(void);

void apic_cleanup_timer(void);
void apic_setup_timer(int vector, u32 mode);

void apic_start_timer(u32 counter);
void apic_stop_timer(void);

/* Converts byte-addressable APIC register offset to 4-byte offset. */
static inline u32 apic_reg_index(u32 reg)
{
	return reg >> 2;
}

static inline u32 x2apic_msr(u32 reg)
{
	return APIC_BASE_MSR + (reg >> 4);
}

static inline bool apic_lvt_entry_supported(int idx)
{
	return GET_APIC_MAXLVT(apic_read(APIC_LVR)) >= idx;
}

static inline u8 task_priority_class(u8 vector)
{
	return vector >> 4;
}

enum x2apic_reg_semantics {
	X2APIC_INVALID	= 0,
	X2APIC_READABLE	= BIT(0),
	X2APIC_WRITABLE	= BIT(1),
	X2APIC_RO	= X2APIC_READABLE,
	X2APIC_WO	= X2APIC_WRITABLE,
	X2APIC_RW	= X2APIC_READABLE | X2APIC_WRITABLE,
};

static inline enum x2apic_reg_semantics get_x2apic_reg_semantics(u32 reg)
{
	assert(!(reg & 0xf));

	switch (reg) {
	case APIC_ID:
	case APIC_LVR:
	case APIC_PROCPRI:
	case APIC_LDR:
	case APIC_ISR ... APIC_ISR + 0x70:
	case APIC_TMR ... APIC_TMR + 0x70:
	case APIC_IRR ... APIC_IRR + 0x70:
	case APIC_TMCCT:
		return X2APIC_RO;
	case APIC_TASKPRI:
	case APIC_SPIV:
	case APIC_ESR:
	case APIC_ICR:
	case APIC_LVTT:
	case APIC_LVTTHMR:
	case APIC_LVTPC:
	case APIC_LVT0:
	case APIC_LVT1:
	case APIC_LVTERR:
	case APIC_TMICT:
	case APIC_TDCR:
		return X2APIC_RW;
	case APIC_EOI:
	case APIC_SELF_IPI:
		return X2APIC_WO;
	case APIC_CMCI:
		if (apic_lvt_entry_supported(6))
			return X2APIC_RW;
		break;
	case APIC_RRR:
	case APIC_DFR:
	case APIC_ICR2:
	default:
		break;
	}
	return X2APIC_INVALID;
}

#endif