2022-06-10 00:22:14 +08:00

186 lines
6.3 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "HPET.h"
#include <common/kprint.h>
#include <mm/mm.h>
#include <driver/interrupt/apic/apic.h>
#include <exception/softirq.h>
#include <driver/timers/timer.h>
#include <process/process.h>
#include <sched/sched.h>
#include <smp/ipi.h>
#include <driver/video/video.h>
static struct acpi_HPET_description_table_t *hpet_table;
static uint64_t HPET_REG_BASE = 0;
static uint32_t HPET_COUNTER_CLK_PERIOD = 0; // 主计数器时间精度(单位:飞秒)
static double HPET_freq = 0; // 主计时器频率
static uint8_t HPET_NUM_TIM_CAP = 0; // 定时器数量
extern struct rtc_time_t rtc_now; // 导出全局墙上时钟
enum
{
GCAP_ID = 0x00,
GEN_CONF = 0x10,
GINTR_STA = 0x20,
MAIN_CNT = 0xf0,
TIM0_CONF = 0x100,
TIM0_COMP = 0x108,
TIM1_CONF = 0x120,
TIM1_COMP = 0x128,
TIM2_CONF = 0x140,
TIM2_COMP = 0x148,
TIM3_CONF = 0x160,
TIM3_COMP = 0x168,
TIM4_CONF = 0x180,
TIM4_COMP = 0x188,
TIM5_CONF = 0x1a0,
TIM5_COMP = 0x1a8,
TIM6_CONF = 0x1c0,
TIM6_COMP = 0x1c8,
TIM7_CONF = 0x1e0,
TIM7_COMP = 0x1e8,
};
hardware_intr_controller HPET_intr_controller =
{
.enable = apic_ioapic_enable,
.disable = apic_ioapic_disable,
.install = apic_ioapic_install,
.uninstall = apic_ioapic_uninstall,
.ack = apic_ioapic_edge_ack,
};
void HPET_handler(uint64_t number, uint64_t param, struct pt_regs *regs)
{
// printk("(HPET)");
switch (param)
{
case 0: // 定时器0中断
++timer_jiffies;
/*
// 将HEPT中断消息转发到ap:1处理器
ipi_send_IPI(DEST_PHYSICAL, IDLE, ICR_LEVEL_DE_ASSERT, EDGE_TRIGGER, 0xc8,
ICR_APIC_FIXED, ICR_ALL_EXCLUDE_Self, true, 0);
*/
// 若当前时间比定时任务的时间间隔大,则进入中断下半部
if (container_of(list_next(&timer_func_head.list), struct timer_func_list_t, list)->expire_jiffies <= timer_jiffies)
set_softirq_status((1 << TIMER_SIRQ));
// if (current_pcb->pid == 2)
// kwarn("timer_jiffies = %ld video_refresh_expire_jiffies=%ld", timer_jiffies, video_refresh_expire_jiffies);
// 当时间到了,或进程发生切换时,刷新帧缓冲区
if (timer_jiffies >= video_refresh_expire_jiffies || (video_last_refresh_pid != current_pcb->pid))
{
raise_softirq(VIDEO_REFRESH_SIRQ);
}
sched_update_jiffies();
break;
default:
kwarn("Unsupported HPET irq: %d.", number);
break;
}
}
int HPET_init()
{
kinfo("Initializing HPET...");
// 从acpi获取hpet结构体
ul hpet_table_addr = 0;
acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
// ACPI表没有HPET尝试读HPTC
if (hpet_table_addr == 0)
{
kwarn("ACPI: HPET Table Not Found On This Computer!");
if (RCBA_vaddr != 0)
{
kerror("NO HPET found on this computer!");
uint32_t *hptc = (uint32_t *)(RCBA_vaddr + 0x3404UL);
// enable HPET
io_mfence();
// 读取HPET配置寄存器地址
switch ((*hptc) & 0x3)
{
case 0:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
break;
case 1:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed01000;
break;
case 2:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed02000;
break;
case 3:
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed03000;
break;
default:
break;
}
// enable HPET
*hptc = 0x80;
io_mfence();
}
else
{
// 没有RCBA寄存器采用默认值
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + 0xfed00000;
kwarn("There is no RCBA register on this computer, and HPET regs base use default value.");
}
}
else // ACPI表中有HPET表
{
hpet_table = (struct acpi_HPET_description_table_t *)hpet_table_addr;
// kdebug("hpet_table_addr=%#018lx", hpet_table_addr);
// 由于这段内存与io/apic的映射在同一物理页内因此不需要重复映射
HPET_REG_BASE = SPECIAL_MEMOEY_MAPPING_VIRT_ADDR_BASE + hpet_table->address;
}
// 读取计时精度并计算频率
uint64_t tmp;
tmp = *(uint64_t *)(HPET_REG_BASE + GCAP_ID);
HPET_COUNTER_CLK_PERIOD = (tmp >> 32) & 0xffffffff;
HPET_freq = 1.0 * 1e15 / HPET_COUNTER_CLK_PERIOD;
HPET_NUM_TIM_CAP = (tmp >> 8) & 0x1f; // 读取计时器数量
// kinfo("HPET CLK_PERIOD=%#03lx Frequency=%f", HPET_COUNTER_CLK_PERIOD, (double)HPET_freq);
// kdebug("HPET_freq=%ld", (long)HPET_freq);
// kdebug("HPET_freq=%lf", HPET_freq);
struct apic_IO_APIC_RTE_entry entry;
// 使用I/O APIC 的IRQ2接收hpet定时器0的中断
apic_make_rte_entry(&entry, 34, IO_APIC_FIXED, DEST_PHYSICAL, IDLE, POLARITY_HIGH, IRR_RESET, EDGE_TRIGGER, MASKED, 0);
// 计算HPET0间隔多少个时钟周期触发一次中断
uint64_t clks_to_intr = 0.001 * HPET0_INTERVAL * HPET_freq;
// kdebug("clks_to_intr=%#ld", clks_to_intr);
if (clks_to_intr <= 0 || clks_to_intr > (HPET_freq * 8))
{
kBUG("HPET0: Numof clocks to generate interrupt is INVALID! value=%lld", clks_to_intr);
while (1)
hlt();
}
*(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
io_mfence();
*(uint64_t *)(HPET_REG_BASE + TIM0_CONF) = 0x004c; // 设置定时器0为周期定时边沿触发投递到IO APIC的2号引脚这里有点绕写的是8259的引脚号但是因为禁用了8259因此会被路由到IO APIC的2号引脚
io_mfence();
*(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = clks_to_intr; // 5ms触发一次中断
//*(uint64_t *)(HPET_REG_BASE + TIM0_COMP) = HPET_freq; // 1s触发一次中断
io_mfence();
rtc_get_cmos_time(&rtc_now);
kinfo("HPET Initialized.");
*(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
io_mfence();
// 注册中断
irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
}