|
@@ -0,0 +1,102 @@
|
|
|
+#include "HPET.h"
|
|
|
+#include <common/kprint.h>
|
|
|
+#include <mm/mm.h>
|
|
|
+#include <driver/interrupt/apic/apic.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_color(ORANGE, BLACK, "(HPET)");
|
|
|
+ switch (param)
|
|
|
+ {
|
|
|
+ case 0:
|
|
|
+ rtc_get_cmos_time(&rtc_now);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int HPET_init()
|
|
|
+{
|
|
|
+ // 从acpi获取hpet结构体
|
|
|
+ ul hpet_table_addr = 0;
|
|
|
+ acpi_iter_SDT(acpi_get_HPET, &hpet_table_addr);
|
|
|
+ if (hpet_table_addr == 0)
|
|
|
+ {
|
|
|
+ kerror("HPET Not Found On This Computer!");
|
|
|
+ return E_HPET_INIT_FAILED;
|
|
|
+ }
|
|
|
+ hpet_table = (struct acpi_HPET_description_table_t *)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, 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);
|
|
|
+ // 注册中断
|
|
|
+ irq_register(34, &entry, &HPET_handler, 0, &HPET_intr_controller, "HPET0");
|
|
|
+
|
|
|
+ *(uint64_t *)(HPET_REG_BASE + GEN_CONF) = 3; // 置位旧设备中断路由兼容标志位、定时器组使能标志位
|
|
|
+ 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) = HPET_freq; // 1s触发一次中断
|
|
|
+ io_mfence();
|
|
|
+
|
|
|
+ rtc_get_cmos_time(&rtc_now);
|
|
|
+ *(uint64_t *)(HPET_REG_BASE + MAIN_CNT) = 0;
|
|
|
+ io_mfence();
|
|
|
+
|
|
|
+
|
|
|
+}
|